Android仿IOS的AssistiveTouch的控件EasyTouch实现
概述:
之前我听到过一则新闻,就是说Ipone中的AssistiveTouch的设计初衷是给残疾人使用的。
而这一功能在亚洲(中国)的使用最为频繁。
虽不知道这新闻的可靠性,但无庸置疑的是它的确给我们操作手机带来了非常大的便捷。在这个设计之前。可能比較easy想到的就是建立快捷方式。而快捷方式的操作结果还是要去载入界面(有时可能是繁重的界面)。
一旦走上了这条路。那距离快捷操作的方向可能就渐行渐远了。
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
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
ShowTableView
代码下载:
Android仿IOS的AssistiveTouch的控件EasyTouch实现的更多相关文章
- (转载) Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框
Android 带清除功能的输入框控件ClearEditText,仿IOS的输入框 标签: Android清除功能EditText仿IOS的输入框 2013-09-04 17:33 70865人阅读 ...
- 动手分析安卓仿QQ联系人列表TreeView控件
因项目需要需要用到仿QQ联系人列表的控件样式,于是网上找到一个轮子(https://github.com/TealerProg/TreeView),工作完成现在简单分析一下这个源码. 一. 需要用 ...
- (转载)Android UI设计之AlertDialog弹窗控件
Android UI设计之AlertDialog弹窗控件 作者:qq_27630169 字体:[增加 减小] 类型:转载 时间:2016-08-18我要评论 这篇文章主要为大家详细介绍了Android ...
- IOS版App的控件元素定位
前言 Android版App的控件元素可以通过Android studio自带的工具uiautomatorviewer来协助定位! IOS版App的控件元素可以通过Appium来实现(未实现),或ap ...
- Unity3d IOS中的IGUI控件
Unity3d IOS中的IGUI控件 @灰太龙 群63438968 我讲一下IOS中用的UI,我们采用IGUI,需要使用IGUI的高版本,在Unity3d 4.2中也可以使用的! 之前IGUI有个 ...
- Android仿IOS回弹效果 ScrollView回弹 总结
Android仿IOS回弹效果 ScrollView回弹 总结 应项目中的需求 须要仿IOS 下拉回弹的效果 , 我在网上搜了非常多 大多数都是拿scrollview 改吧改吧 试了一些 发现总 ...
- Android support library支持包常用控件介绍(二)
谷歌官方推出Material Design 设计理念已经有段时间了,为支持更方便的实现 Material Design设计效果,官方给出了Android support design library ...
- Android开发技巧——自定义控件之组合控件
Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...
- Xamarin iOS教程之页面控件
Xamarin iOS教程之页面控件 Xamarin iOS 页面控件 在iPhone手机的主界面中,经常会看到一排小白点,那就是页面控件,如图2.44所示.它是由小白点和滚动视图组成,可以用来控制翻 ...
随机推荐
- P1127 词链
P1127 词链 题目描述 如果单词X的末字母与单词Y的首字母相同,则X与Y可以相连成X.Y.(注意:X.Y之间是英文的句号“.”).例如,单词dog与单词gopher,则dog与gopher可以相连 ...
- python的模块导入
单个文件导入:导入的模块可以是一个py文件(放置在当前文件的同级目录.默认路径等) 导入:import 模块名 使用:模块名.函数名 导入:from 模块名 import * 使用:函数名 ----- ...
- PyQt5.9 Html与本地代码交互实例
在PyQt5.9中, 应用QWebEngineView和QWebChannel技术, 可以进行HTML与本地代码进行交互. 要点: 创建交互对象, 基于QObject, 定义信息槽 创建QWebCha ...
- 2.TinkPHP入门----控制器
1.控制器创建 命名规则:控制器名称+Controller+.class.php, 例如GoodsController.class.php UserController.class.php 控制器结 ...
- C#中图片转换为Base64编码,Base64编码转换为图片
#region 图片转为base64编码的字符串 public string ImgToBase64String(string Imagefilename) { try { Bitmap bmp = ...
- TensorFlow-LSTM序列预测
问题情境:已知某一天内到目前为止股票各个时刻的价格,预测接下来短时间内的价格变化. import tushare as ts import time from collections import n ...
- (转)一个vue路由参数传递的注意点
首先我的路由的定义 { path: '/b', name: 'B', component: resolve => require(['../pages/B.vue'], resolve) } 我 ...
- mysqldump+mydumper+xtrabackup备份原理流程
mysqldump备份原理 备份的基本流程如下: 1.调用FTWRL(flush tables with read lock),全局禁止读写 2.开启快照读,获取此时的快照(仅对innodb表起作用) ...
- 图像压缩Vs.压缩感知
压缩感知科普文两则: 原文链接:http://www.cvchina.info/2010/06/08/compressed-sensing-2/ 这几天由于happyharry的辛勤劳动,大伙纷纷表示 ...
- Java中Scanner类的使用
一个可以解析基本类型和字符串的简单文本扫描器. 例如,以下代码使用户能够从 System.in 中读取一个数: public class ApiScanner { public static void ...