概述:
之前我听到过一则新闻,就是说Ipone中的AssistiveTouch的设计初衷是给残疾人使用的。而这一功能在亚洲(中国)的使用最为频繁。
虽不知道这新闻的可靠性,但无庸置疑的是它的确给我们操作手机带来了很大的便捷。在这个设计之前,可能比较容易想到的就是建立快捷方式,而快捷方式的操作结果还是要去加载界面(有时可能是繁重的界面)。一旦走上了这条路,那距离快捷操作的方向可能就渐行渐远了。
AssistiveTouch的设计的确很赞。Android也是值得拥有这一棒棒的功能,下面我就来简单说明一下在Android上要如何实现这一功能。
思路整理:
一眼看到这样的功能,我们可能困惑的是在Android中要怎么在系统桌面的上方添加控件。是的,这是一个难点。从大小上,可能你想到了Dialog,不过Android中的Dialog可不能在系统的桌面上显示。那你可能又会说不是一种是针对Activity的Dialog主题的模式吗?是的,这样的确是解决了在系统桌面的上方弹出窗口了。可是,我们又要对控件进行随意拖拽,这一点可能对于Android而言并非易事。
但是,Android中允许我们在WindowManager上添加View。Android中的窗口机制就是基于WindowManager实现的。WindowManager的作用就是添加View到屏幕,或是从屏幕中移除View。它是显示View的最底层。
好了,的确是这样的。WindowManger就是实现的关键。下面就来实现它吧。
不过还有一点需要注意,就我们的EasyTouchView是要基于一个常在的Context来创建,如果EasyTouchView基于了像Activity这样的短生命周期的Context创建,那么EasyTouchView就会很快随着Activity的暂停或是销毁而消失。
实现过程:
EasyTouchView:
package com.bumblebee.remindeasy.widgets; import java.util.Timer; import java.util.TimerTask; import com.bumblebee.remindeasy.R; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.os.Handler; import android.os.Message; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.Button; import android.widget.ImageView; import android.widget.PopupWindow; import android.widget.Toast; public class EasyTouchView extends View { private Context mContext; private WindowManager mWManager; private WindowManager.LayoutParams mWMParams; private View mTouchView; private ImageView mIconImageView = null; private PopupWindow mPopuWin; private ServiceListener mSerLisrener; private View mSettingTable; private int mTag = 0; private int midX; private int midY; private int mOldOffsetX; private int mOldOffsetY; private Toast mToast; private Timer mTimer = null; private TimerTask mTask = null; public EasyTouchView(Context context, ServiceListener listener) { super(context); mContext = context; mSerLisrener = listener; } public void initTouchViewEvent() { initEasyTouchViewEvent(); initSettingTableView(); } private void initEasyTouchViewEvent() { // 设置载入view WindowManager参数 mWManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); midX = mWManager.getDefaultDisplay().getWidth() / 2 - 25; midY = mWManager.getDefaultDisplay().getHeight() / 2 - 44; mTouchView = LayoutInflater.from(mContext).inflate(R.layout.easy_touch_view, null); mIconImageView = (ImageView) mTouchView.findViewById(R.id.easy_touch_view_imageview); mTouchView.setBackgroundColor(Color.TRANSPARENT); mTouchView.setOnTouchListener(mTouchListener); WindowManager wm = mWManager; WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams(); mWMParams = wmParams; wmParams.type = 2003; // 这里的2002表示系统级窗口,你也可以试试2003。 wmParams.flags = 40; // 设置桌面可控 wmParams.width = 100; wmParams.height = 100; wmParams.format = -3; // 透明 wm.addView(mTouchView, wmParams); } private void initSettingTableView() { mSettingTable = LayoutInflater.from(mContext).inflate(R.layout.show_setting_table, null); Button commonUseButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_common_use_button); Button screenLockButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_screen_lock_button); Button notificationButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_notification_button); Button phoneButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_phone_button); Button pageButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_page_button); Button cameraButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_camera_button); Button backButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_back_button); Button homeButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_home_button); Button exitTouchButton = (Button) mSettingTable.findViewById(R.id.show_setting_table_item_exit_touch_button); commonUseButton.setOnClickListener(mClickListener); screenLockButton.setOnClickListener(mClickListener); notificationButton.setOnClickListener(mClickListener); phoneButton.setOnClickListener(mClickListener); pageButton.setOnClickListener(mClickListener); cameraButton.setOnClickListener(mClickListener); backButton.setOnClickListener(mClickListener); homeButton.setOnClickListener(mClickListener); exitTouchButton.setOnClickListener(mClickListener); } private OnClickListener mClickListener = new OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.show_setting_table_item_common_use_button: hideSettingTable("常用"); break; case R.id.show_setting_table_item_screen_lock_button: hideSettingTable("锁屏"); break; case R.id.show_setting_table_item_notification_button: hideSettingTable("通知"); break; case R.id.show_setting_table_item_phone_button: hideSettingTable("电话"); break; case R.id.show_setting_table_item_page_button: hideSettingTable("1"); break; case R.id.show_setting_table_item_camera_button: hideSettingTable("相机"); break; case R.id.show_setting_table_item_back_button: hideSettingTable("返回"); break; case R.id.show_setting_table_item_home_button: hideSettingTable("主页"); break; case R.id.show_setting_table_item_exit_touch_button: quitTouchView(); break; } } }; private void quitTouchView() { hideSettingTable("退出"); mWManager.removeView(mTouchView); mSerLisrener.OnCloseService(true); clearTimerThead(); } private OnTouchListener mTouchListener = new OnTouchListener() { float lastX, lastY; int paramX, paramY; public boolean onTouch(View v, MotionEvent event) { final int action = event.getAction(); float x = event.getRawX(); float y = event.getRawY(); if (mTag == 0) { mOldOffsetX = mWMParams.x; // 偏移量 mOldOffsetY = mWMParams.y; // 偏移量 } switch (action) { case MotionEvent.ACTION_DOWN: motionActionDownEvent(x, y); break; case MotionEvent.ACTION_MOVE: motionActionMoveEvent(x, y); break; case MotionEvent.ACTION_UP: motionActionUpEvent(x, y); break; default: break; } return true; } private void motionActionDownEvent(float x, float y) { lastX = x; lastY = y; paramX = mWMParams.x; paramY = mWMParams.y; } private void motionActionMoveEvent(float x, float y) { int dx = (int) (x - lastX); int dy = (int) (y - lastY); mWMParams.x = paramX + dx; mWMParams.y = paramY + dy; mTag = 1; // 更新悬浮窗位置 mWManager.updateViewLayout(mTouchView, mWMParams); } private void motionActionUpEvent(float x, float y) { int newOffsetX = mWMParams.x; int newOffsetY = mWMParams.y; if (mOldOffsetX == newOffsetX && mOldOffsetY == newOffsetY) { mPopuWin = new PopupWindow(mSettingTable, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); mPopuWin.setTouchInterceptor(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { hideSettingTable(); return true; } return false; } }); mPopuWin.setBackgroundDrawable(new BitmapDrawable()); mPopuWin.setTouchable(true); mPopuWin.setFocusable(true); mPopuWin.setOutsideTouchable(true); mPopuWin.setContentView(mSettingTable); if (Math.abs(mOldOffsetX) > midX) { if (mOldOffsetX > 0) { mOldOffsetX = midX; } else { mOldOffsetX = -midX; } } if (Math.abs(mOldOffsetY) > midY) { if (mOldOffsetY > 0) { mOldOffsetY = midY; } else { mOldOffsetY = -midY; } } mPopuWin.setAnimationStyle(R.style.AnimationPreview); mPopuWin.setFocusable(true); mPopuWin.update(); mPopuWin.showAtLocation(mTouchView, Gravity.CENTER, -mOldOffsetX, -mOldOffsetY); if (mTimer == null) { catchSettingTableDismiss(); } } else { mTag = 0; } } }; private void catchSettingTableDismiss() { mTimer = new Timer(); mTask = new TimerTask() { @Override public void run() { if (mPopuWin == null || !mPopuWin.isShowing()) { handler.sendEmptyMessage(0x0); } else { handler.sendEmptyMessage(0x1); } } }; mTimer.schedule(mTask, 0, 100); } private void clearTimerThead() { if (mTask != null) { mTask.cancel(); mTask = null; } if (mTimer != null) { mTimer.cancel(); mTimer = null; } } Handler handler = new Handler() { public void handleMessage(Message msg) { if (msg.what == 0x0) { mIconImageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.touch_ic)); } else if (msg.what == 0x1) { mIconImageView.setBackgroundDrawable(getResources().getDrawable(R.drawable.transparent)); } }; }; public void showToast(Context context, String text) { if (mToast == null) { mToast = Toast.makeText(context, text, Toast.LENGTH_SHORT); } else { mToast.setText(text); mToast.setDuration(Toast.LENGTH_SHORT); } mToast.show(); } private void hideSettingTable(String content) { hideSettingTable(); showToast(mContext, content); } private void hideSettingTable() { if (null != mPopuWin) { mPopuWin.dismiss(); } } public interface ServiceListener { public void OnCloseService(boolean isClose); } }
AuxiliaryService:
public class AuxiliaryService extends Service implements ServiceListener { private Intent mIntent; @Override public IBinder onBind(Intent intent) { return null; } public void onCreate() { super.onCreate(); new EasyTouchView(this, this).initTouchViewEvent(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { mIntent = intent; return super.onStartCommand(intent, flags, startId); } @Override public void OnCloseService(boolean isClose) { stopService(mIntent); } }
这里有一点需要注意一下。大家可以通过上面的代码看出,我们启动EasyTouchView是通过Service来启动的。一般的EasyTouch都会提供一个锁屏的功能。要使用一键锁屏就需要激活设备管理器,就要去跳转到系统的一些界面,而这些界面的启动不可以是基于Service的,需要基于Activity来做处理。基于Service启动的过程是闪烁一下后就消失了。
这里我们可以在Service中启动一个我们自己的Activity,然后在这个Activity中启动这个设置设备管理器的界面。
代码如下:
public class AuxiliaryActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); lockScreen(); } private void lockScreen() { DevicePolicyManager mDevicePolicyManager; ComponentName mComponentName; mDevicePolicyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); mComponentName = new ComponentName(this, LockReceiver.class); // 判断是否有权限 if (mDevicePolicyManager.isAdminActive(mComponentName)) { mDevicePolicyManager.lockNow(); finish(); } else { activeManager(mComponentName); } } /** * 激活设备管理器获取权限 */ private void activeManager(ComponentName componentName) { Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName); intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "One key lock the screen"); startActivity(intent); finish(); } }
效果图:
TouchView
ShowTableView