Android开发:智能TV发现、配对及遥控器功能实现求助
实现智能TV发现、配对与遥控功能步骤
一、局域网智能TV设备发现
Android平台推荐用**Network Service Discovery(NSD)**实现同局域网设备发现,步骤如下:
- 初始化NSD管理器
private NsdManager nsdManager; private NsdManager.DiscoveryListener discoveryListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); nsdManager = (NsdManager) getSystemService(Context.NSD_SERVICE); initializeDiscoveryListener(); }
- 实现DiscoveryListener监听设备发现事件
private void initializeDiscoveryListener() { discoveryListener = new NsdManager.DiscoveryListener() { @Override public void onDiscoveryStarted(String serviceType) { // 开始发现设备的回调 } @Override public void onServiceFound(NsdServiceInfo serviceInfo) { // 筛选智能TV(可根据服务类型或设备名称过滤) if (serviceInfo.getServiceType().contains("_androidtv._tcp.") || serviceInfo.getServiceName().contains("TV")) { // 将设备信息存入自定义实体类列表 tvDeviceList.add(new TvDevice(serviceInfo.getServiceName(), serviceInfo.getHost().getHostAddress(), serviceInfo.getPort())); // 更新RecyclerView tvAdapter.notifyDataSetChanged(); } } @Override public void onServiceLost(NsdServiceInfo serviceInfo) { // 设备离开网络,移除对应列表项 } @Override public void onDiscoveryStopped(String serviceType) { // 停止发现的回调 } @Override public void onStartDiscoveryFailed(String serviceType, int errorCode) { // 发现启动失败的回调 } @Override public void onStopDiscoveryFailed(String serviceType, int errorCode) { // 发现停止失败的回调 } }; }
- 启动/停止设备发现
@Override protected void onResume() { super.onResume(); nsdManager.discoverServices("_androidtv._tcp.", NsdManager.PROTOCOL_DNS_SD, discoveryListener); } @Override protected void onPause() { super.onPause(); if (nsdManager != null) { nsdManager.stopServiceDiscovery(discoveryListener); } }
二、用RecyclerView展示TV列表
- 创建自定义TvDevice实体类
public class TvDevice implements Serializable { private String name; private String ip; private int port; public TvDevice(String name, String ip, int port) { this.name = name; this.ip = ip; this.port = port; } // getter方法 public String getName() { return name; } public String getIp() { return ip; } public int getPort() { return port; } }
- 实现RecyclerView Adapter
public class TvListAdapter extends RecyclerView.Adapter<TvListAdapter.TvViewHolder> { private List<TvDevice> tvDeviceList; private OnTvItemClickListener listener; public interface OnTvItemClickListener { void onTvItemClick(TvDevice device); } public TvListAdapter(List<TvDevice> tvDeviceList, OnTvItemClickListener listener) { this.tvDeviceList = tvDeviceList; this.listener = listener; } @NonNull @Override public TvViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_tv_device, parent, false); return new TvViewHolder(view); } @Override public void onBindViewHolder(@NonNull TvViewHolder holder, int position) { TvDevice device = tvDeviceList.get(position); holder.tvName.setText(device.getName()); holder.tvIp.setText(device.getIp()); holder.itemView.setOnClickListener(v -> listener.onTvItemClick(device)); } @Override public int getItemCount() { return tvDeviceList.size(); } static class TvViewHolder extends RecyclerView.ViewHolder { TextView tvName; TextView tvIp; public TvViewHolder(@NonNull View itemView) { super(itemView); tvName = itemView.findViewById(R.id.tv_device_name); tvIp = itemView.findViewById(R.id.tv_device_ip); } } }
- 列表项布局
item_tv_device.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="16dp" android:orientation="vertical"> <TextView android:id="@+id/tv_device_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="18sp" android:textStyle="bold"/> <TextView android:id="@+id/tv_device_ip" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="14sp" android:textColor="@android:color/darker_gray"/> </LinearLayout>
- 初始化RecyclerView
private RecyclerView rvTvList; private TvListAdapter tvAdapter; private List<TvDevice> tvDeviceList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); rvTvList = findViewById(R.id.rv_tv_list); rvTvList.setLayoutManager(new LinearLayoutManager(this)); tvAdapter = new TvListAdapter(tvDeviceList, this::onTvItemClick); rvTvList.setAdapter(tvAdapter); } // 点击TV项触发配对流程 private void onTvItemClick(TvDevice device) { startPairing(device); }
三、设备配对流程
以Socket连接+PIN码验证为例(通用方案):
- 建立Socket连接到TV
private Socket tvSocket; private DataOutputStream outputStream; private void startPairing(TvDevice device) { new Thread(() -> { try { tvSocket = new Socket(device.getIp(), device.getPort()); outputStream = new DataOutputStream(tvSocket.getOutputStream()); // 发送配对请求(自定义协议) outputStream.writeUTF("PAIR_REQUEST"); outputStream.flush(); // 接收TV返回的PIN码 DataInputStream inputStream = new DataInputStream(tvSocket.getInputStream()); String tvPin = inputStream.readUTF(); // 主线程显示PIN输入对话框 runOnUiThread(() -> showPinInputDialog(tvPin, device)); } catch (IOException e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(this, "配对连接失败", Toast.LENGTH_SHORT).show()); } }).start(); }
- PIN码输入与验证
private void showPinInputDialog(String tvPin, TvDevice device) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("输入配对PIN码"); builder.setMessage("TV显示的PIN码:" + tvPin); EditText input = new EditText(this); builder.setView(input); builder.setPositiveButton("确认", (dialog, which) -> { String userPin = input.getText().toString(); if (userPin.equals(tvPin)) { new Thread(() -> { try { outputStream.writeUTF("PAIR_SUCCESS"); outputStream.flush(); runOnUiThread(() -> { Toast.makeText(this, "配对成功", Toast.LENGTH_SHORT).show(); // 跳转至遥控界面 startActivity(new Intent(this, RemoteControlActivity.class) .putExtra("tv_device", device)); }); } catch (IOException e) { e.printStackTrace(); } }).start(); } else { Toast.makeText(this, "PIN码错误", Toast.LENGTH_SHORT).show(); } }); builder.setNegativeButton("取消", null); builder.show(); }
四、遥控器功能实现
配对成功后通过Socket发送按键指令:
- 遥控界面布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:id="@+id/btn_up" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:text="↑"/> <Button android:id="@+id/btn_left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toLeftOf="@id/btn_ok" android:layout_centerVertical="true" android:text="←"/> <Button android:id="@+id/btn_ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="确认"/> <Button android:id="@+id/btn_right" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/btn_ok" android:layout_centerVertical="true" android:text="→"/> <Button android:id="@+id/btn_down" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_below="@id/btn_ok" android:text="↓"/> </RelativeLayout>
- 遥控逻辑实现
public class RemoteControlActivity extends AppCompatActivity { private Socket tvSocket; private DataOutputStream outputStream; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_remote_control); TvDevice device = (TvDevice) getIntent().getSerializableExtra("tv_device"); // 重新建立Socket连接 new Thread(() -> { try { tvSocket = new Socket(device.getIp(), device.getPort()); outputStream = new DataOutputStream(tvSocket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } }).start(); // 绑定按键事件 findViewById(R.id.btn_up).setOnClickListener(v -> sendKeyCommand("KEY_UP")); findViewById(R.id.btn_down).setOnClickListener(v -> sendKeyCommand("KEY_DOWN")); findViewById(R.id.btn_left).setOnClickListener(v -> sendKeyCommand("KEY_LEFT")); findViewById(R.id.btn_right).setOnClickListener(v -> sendKeyCommand("KEY_RIGHT")); findViewById(R.id.btn_ok).setOnClickListener(v -> sendKeyCommand("KEY_OK")); } private void sendKeyCommand(String command) { new Thread(() -> { try { if (outputStream != null) { outputStream.writeUTF(command); outputStream.flush(); } } catch (IOException e) { e.printStackTrace(); runOnUiThread(() -> Toast.makeText(this, "指令发送失败", Toast.LENGTH_SHORT).show()); } }).start(); } @Override protected void onDestroy() { super.onDestroy(); // 关闭连接释放资源 try { if (tvSocket != null) tvSocket.close(); if (outputStream != null) outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
注意:不同品牌TV的服务类型、配对协议、遥控指令存在差异,实际开发需参考对应厂商开放API。通用Android TV也可通过ADB命令实现遥控(需TV开启ADB调试),示例命令:
adb shell input keyevent KEYCODE_DPAD_UP,可通过Java执行shell命令发送。
内容的提问来源于stack exchange,提问作者Hayat
相关产品推荐
相关产品推荐

