老规矩先看需求:在最近任务上面添加锁定功能,基于安卓7.0的源码,效果图在最后
分析移除任务的操作有哪些?
1.点击“X”按钮
2.点击“全部清除”
3.左右滑动
好啦!知道那里可以移除,在哪里修改就好了
首先你要知道最近任务在哪个包下面,不卖关子,告诉你
在frameworks\base\packages\SystemUI文件夹下,包名是com.android.systemui
上来二话不说先添加一个bool值再说,是为了多版本适配
具体可以参考 Mr.Nubility进阶记——安卓系统开发之多版本适配 这篇文章
接下来要增加锁 我们需要先找到控件所在的地方,下面提供两种方法
1.打开String.xml文件,搜索“全部清除”,然后拿到id,全局搜索看哪里用到的
2.打开AS->tools->Android->Android Device Monitor,点击界面上你要找的按钮,你想要什么自己拿就好了
通过上述两种方法可以找到布局文件recents_task_view_header.xml,在后面添加一个ImageView就好了
至于锁的图片,有美工的找美工要,没有美工的,找我要哈哈,网址如下
阿里巴巴的http://iconfont.cn/?utm_source=androiddevtools&utm_medium=website
布局画好了,开始逻辑吧,一步一步来不着急
根据布局里的ID 我们可以轻松找到TaskViewHeader.java,然后先添加imageview自身的逻辑
为了保证代码的可读性,我们尽量按照源码的方式写,只写关键代码
protected void onFinishInflate() { mFivLock = (FixedSizeImageView) findViewById(R.id.fiv_lock);}
public void bindToTask(Task t, boolean touchExplorationEnabled, boolean disabledInSafeMode) {
...//判断是否显示lock
if (getResources().getBoolean(com.android.systemui.R.bool.enable_lock_recent_task)) {
//设置Tag共点击时图标切换使用
mFivLock.setTag(Prefs.getBoolean(mContext, t.title, false) ? LOCK : LOCK_OFF);
mFivLock.setVisibility(View.VISIBLE);
mFivLock.setOnClickListener(this);
}
}
public void onTaskDataLoaded() {
mFivLock.setImageResource(Prefs.getBoolean(mContext, mTask.title, false) ? R.drawable.lock : R.drawable.lock_off);
}
public void unbindFromTask(boolean touchExplorationEnabled) {
mTask = null;
mFivLock.setImageDrawable(null);
if (touchExplorationEnabled) {
mFivLock.setClickable(false);
}
}
public void onClick(View v) {
if (v == mIconView) {
// In accessibility, a single click on the focused app info button will show it
EventBus.getDefault().send(new ShowApplicationInfoEvent(mTask));
} else if (v == mDismissButton) {
TaskView tv = Utilities.findParent(this, TaskView.class);
if (!Prefs.getBoolean(mContext, mTask.title, false)) {
tv.dismissTask();
// Keep track of deletions by the dismiss button
MetricsLogger.histogram(getContext(), "overview_task_dismissed_source",
Constants.Metrics.DismissSourceHeaderButton);
} else {
mAnimationHelper.startLockAnimation(tv);
}
..........省略
} else if (v == mFivLock) {
if (v.getTag().equals(LOCK_OFF)) {
mFivLock.setImageResource(R.drawable.lock);
mFivLock.setTag(LOCK);
Prefs.putBoolean(mContext, mTask.title, true);
} else {
mFivLock.setImageResource(R.drawable.lock_off);
mFivLock.setTag(LOCK_OFF);
Prefs.putBoolean(mContext, mTask.title, false);
}
}
}
到此,我们已经可以点击图标切换图片了,只是锁住之后还是可以被清除而已
细心的会发现代码中下面两句代码
Prefs.putBoolean(mContext, mTask.title, false);
Prefs.getBoolean(mContext, t.title, false)
这个就是最最关键的当前Task的状态记录了,判断能不能被清除,就是通过这个判断的
这里我采用的是SharedPreferences的存储方式,其实这是当初的备选方案
开始的时候我是采用 在Task.java中设置set/get属性,然后去判断,但是后面在使用分屏功能后,
Task的状态被清除了,导致不准确,故而采用SP的存储方式
1.点击“X”按钮的处理
上面onClick的代码里面其实已经写了的,很简单,只需要加个判断就好了
Prefs.getBoolean(mContext, mTask.title, false)
当不可以清除的时候,toast一下就可以了,这里我是做了一个左右摇晃的动画
public void startLockAnimation(View view) {
PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe(View.TRANSLATION_X,
Keyframe.ofFloat(0f, 0f),
Keyframe.ofFloat(0.2f, -distance),
Keyframe.ofFloat(0.4f, distance),
Keyframe.ofFloat(0.6f, -distance),
Keyframe.ofFloat(0.8f, distance),
Keyframe.ofFloat(1.0f, 0f)
);
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
objectAnimator.setDuration(duration);
objectAnimator.start();
}
2.点击“全部清除”的处理
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
...
LayoutInflater inflater = LayoutInflater.from(context);
mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,this, false)
mStackActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().send(new DismissAllTaskViewsEvent());
}
});
...
}
找到了,在RecentsView.java中,点击的时候发了一个消息,仔细一看,我屮艸芔茻,EventBus,可以的,我喜欢
全局搜索DismissAllTaskViewsEvent,你会发现好多地方接收,最终根据逻辑确定TaskStackView.java
public final void onBusEvent(final DismissAllTaskViewsEvent event) {
ArrayList tasks = new ArrayList<>(mStack.getStackTasks());
for (int i = tasks.size() - 1; i >= 0; i--) {
Task task = tasks.get(i);
if (!Prefs.getBoolean(mContext, task.title, false)) {
//这里必须设置动画,并且是用手动滑动删除,点击按钮删除的动画Interpolators.FAST_OUT_SLOW_IN
//不然比如一共10个任务,将第一个,最后一个锁住,这时候点击全部清除的话,中间被删除的还会占着原来的空间
//加上这个动画后,每次清除完成都会重新排版,两个TaskView会紧挨在一起
mStack.removeTask(task, new AnimationProps(TaskStackView.DEFAULT_SYNC_STACK_DURATION,
Interpolators.FAST_OUT_SLOW_IN), false);
EventBus.getDefault().send(new DeleteTaskDataEvent(task));
}
}
// 为了执行清除时的抖动动画,这里创建了一个用于存储锁住的taskView
ArrayList lockedViews = new ArrayList<>();
//getTaskViews()这个方法获得的是你能看到的TaskView 如果任务多了的话就 没显示的不在这里面
for (TaskView view : getTaskViews()) {
if (!Prefs.getBoolean(mContext, view.getTask().title, false)) {
unlockedViews.add(view);
} else {
lockedViews.add(view);
}
}
//这个动画放在最后执行,可以保证原本看不到的TaskView,重新回到视线可以看见的时候也会抖动
mAnimationHelper.startLockAnimation(lockedViews);
}
删除代码 有兴趣可以自己看一下
public final void onBusEvent(DeleteTaskDataEvent event) {
// Remove any stored data from the loader
RecentsTaskLoader loader = Recents.getTaskLoader();
loader.deleteTaskData(event.task, false);
// Remove the task from activity manager
SystemServicesProxy ssp = Recents.getSystemServices();
ssp.removeTask(event.task.key.id);
}
3.左右滑动的处理
这个看得我比较烦躁,各种事件分发、拦截,但是也并不难处理,打日志就可以了
事件分发拦截,可以参考这篇文章 ************************
最终锁定处理逻辑在,SwipeHelper.java中
public boolean onTouchEvent(MotionEvent ev) {
...
case MotionEvent.ACTION_CANCEL:
... {
if (isDismissGesture(ev)) {
dismissChild(mCurrView, velocity,
!swipedFastEnough() /* useAccelerateInterpolator */);
} else {
mCallback.onDragCancelled(mCurrView);
snapChild(mCurrView, 0 /* leftTarget */, velocity);
}
...
}
return true;
}
判断是不是删除的手势,比如很快,很远等
protected boolean isDismissGesture(MotionEvent ev) {
boolean falsingDetected = mCallback.isAntiFalsingNeeded();
if (mFalsingManager.isClassiferEnabled()) {
falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
} else {
falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
}
// add by HZH on 2018/1/18 start
TaskView taskView = (TaskView) mCurrView;
boolean isLocked = Prefs.getBoolean(mContext, taskView.getTask().title, false);
return !isLocked
&& !falsingDetected
&& (swipedFastEnough() || swipedFarEnough())
&& ev.getActionMasked() == MotionEvent.ACTION_UP
&& mCallback.canChildBeDismissed(mCurrView);
}
到此,功能已经完成,收工!以前弄得,现在可能还有人在用7.0的系统,姑且放出来,下面是效果图