android 投屏,华为手机的电脑模式是如何实现的
1.前言
? 不知道大家在手机往家里电视投屏的时候有没有发现,华为手机和三星手机投屏成功后,会有手机和电脑两种模式,手机模式是将手机镜像投到屏幕上,而电脑模式,则是将大屏电视变为一个电脑一样,如果连接鼠标或者大屏支持触控,则通过电脑模式是可以直接操作手机的。此时手机只要不关机,是可以继续干其他事情的。这就相当于一个系统分两个用。真是不要太牛逼。
? 当然还有另外一种投屏方式,应该是大家用的最多的,就是使用DLNA 投屏,将手机播放的视屏,图片,音乐等多媒体投屏到大屏设备播放,此时手机还可以继续干其他事情,甚至关机,也不影响DLNA 的播放。
2. 投屏模式
-
- 手机模式
-
- 电脑模式
-
- DLNA
如上图播放视屏时点击右上角TV 投屏按钮,即可开启DLNA投屏。 3.投屏区分
1. DLNA
主要用于视屏,音乐,图片等多媒体的投射。投射时手机和大屏必须在同一局域网中。主要原理还是udp。手机投屏大屏后,手机就可干其他事情,哪怕手机关机,大屏也不会停止播放。
现今社会,基本上视屏,电视,电影,都不是免费的,都需要开会员,而且更可恶的是,同一家app,例如某讯,在电视端和手机端的资源是不互通的。这样就显得DLNA 尤为重要了。
2.Miracast
通过手机setting或者下拉菜单栏里面的投屏进行投屏的,就是用的miracast 协议。 这里面又分手机模式和电脑模式。google 原生支持手机模式镜像。电脑模式目前好像只有华为手机和三星手机支持。
手机模式也就是投屏手机镜像,会将手机的实时页面,同步投屏到大屏上,手机上显示什么,大屏上就显示什么,手机锁屏,大屏锁屏,手机息屏,大屏息屏。
而电脑模式就很像是上面的DLNA 一样,会将一个特定的页面投给大屏,而此时手机可以干其他的事,但不能断开投屏连接。我把这个说为静态页面,把DLNA 那个说为动态页面。
4.进入正题,miracast 投屏电脑模式实现。
与其说是电脑模式实现,不如说是对电脑模式实现的一点思路。因为我也不知道华为是怎么实现的。但我觉得他就说这么实现的。 下面直接上代码:
package com.example.miracast;
import android.app.Activity;
import android.app.Presentation;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.util.SparseArray;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
public class MainActivity extends Activity {
private DisplayManager mDisplayManager;
private DisplayListAdapter mDisplayListAdapter;
private ListView mListView;
private final SparseArray<RemotePresentation> mActivePresentations = new SparseArray<RemotePresentation>();
private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() {
@Override
public void onDisplayAdded(int displayId) {
mDisplayListAdapter.updateContents();
}
@Override
public void onDisplayChanged(int displayId) {
mDisplayListAdapter.updateContents();
}
@Override
public void onDisplayRemoved(int displayId) {
mDisplayListAdapter.updateContents();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.local_display);
mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
mDisplayListAdapter = new DisplayListAdapter(this);
mListView = (ListView) findViewById(R.id.display_list);
mListView.setAdapter(mDisplayListAdapter);
}
@Override
protected void onResume() {
super.onResume();
mDisplayListAdapter.updateContents();
mDisplayManager.registerDisplayListener(mDisplayListener, null);
}
private void showPresentation(Display display) {
RemotePresentation presentation = new RemotePresentation(this, display);
mActivePresentations.put(display.getDisplayId(), presentation);
presentation.show();
}
private void hidePresentation(Display display) {
final int displayId = display.getDisplayId();
RemotePresentation presentation = mActivePresentations.get(displayId);
if (presentation == null) {
return;
}
presentation.dismiss();
mActivePresentations.delete(displayId);
}
private final class DisplayListAdapter extends ArrayAdapter<Display> {
final Context mContext;
private OnCheckedChangeListener mCheckedRemoteDisplay = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton view, boolean isChecked) {
synchronized (mCheckedRemoteDisplay) {
final Display display = (Display) view.getTag();
if (isChecked) {
showPresentation(display);
} else {
hidePresentation(display);
}
}
}
};
public DisplayListAdapter(Context context) {
super(context, R.layout.list_item);
mContext = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View v;
if (convertView == null) {
v = ((Activity) mContext).getLayoutInflater().inflate(R.layout.list_item, null);
} else {
v = convertView;
}
final Display display = getItem(position);
TextView tv = (TextView) v.findViewById(R.id.display_id);
tv.setText(display.getName() + "( ID: " + display.getDisplayId() + " )");
tv = (TextView) v.findViewById(R.id.display_desc);
tv.setText(display.toString());
CheckBox cb = (CheckBox) v.findViewById(R.id.display_cb);
cb.setTag(display);
cb.setOnCheckedChangeListener(mCheckedRemoteDisplay);
return v;
}
public void updateContents() {
clear();
Display[] displays = mDisplayManager.getDisplays();
addAll(displays);
}
}
private final class RemotePresentation extends Presentation {
public RemotePresentation(Context context, Display display) {
super(context, display);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.remote_display);
}
}
}
remote_display.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textSize="22sp"
android:textStyle="bold"
android:text="Hello world, Remote Display over Miracast!!" />
</RelativeLayout>
local_display.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<ListView
android:id="@+id/display_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<CheckBox
android:id="@+id/display_cb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:checked="false"/>
<TextView
android:id="@+id/display_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/display_cb"
android:layout_alignBaseline="@+id/display_cb"
android:textSize="18sp"
android:textColor="#000000"/>
<TextView
android:id="@+id/display_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/display_cb"
android:textSize="14sp"
android:textColor="#303030"/>
</RelativeLayout>
这样就实现了,投屏到大屏上后,小屏依然可以干其他事情。这样我们就可以把上面那个remote_display页面写成和电脑的window 桌面一样的launcher。
|