一步步实现仿微信左滑动返回效果(二)
- 本系列博文将让您学习了解到微信的左滑动返回交互式体验的实现以及基本原理
- 本系列博文将一步一步带领您完成此效果的具体实现方式
前言
上文中带大家简单分析了一下该效果实现的基本原理,接下来我们开始一步步实现完善左滑动返回效果。
本篇博文将完成Activity之间的左滑动返回效果
基本准备
1.当然是新建一个项目啦,名字还不是各位大佬开心就好。
2.建立至少三个Activity,以便进行测试。
第一步 透明化Activity主题
1.在style.xml中继承AppTheme,增加windowIsTranslucent为投true属性
<style name="TranslucentTheme" parent="AppTheme">
<item name="android:windowIsTranslucent">true</item>
</style>
2.建立BaseActivity,为onCreate增加逻辑
public class SlideBaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置window为透明
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
}
}
至此透明化Activity主题已完成
第二步 建立包裹主内容区布局
1.我们新建一个Layout类继承于FrameLayout,并开始完成初始操作逻辑
SlideBackLayout.java
public class SlideBackLayout extends FrameLayout implements ViewDragHelperCallBack.ViewDragLisListener {
private final static float DEFAULT_SENSITIVITY=1.0f;
private View decorView;//主内容布局
private ViewDragHelper dragHelper;//拖动核心帮助类
private ViewDragHelperCallBack callBack;//回调处理类
private boolean canSlide=true;//是否允许滑动,默认为允许滑动
private Activity mActivity;
public SlideBackLayout(Context context) {
super(context);
inti();
}
public void setCanSlide(boolean canSlide) {
this.canSlide = canSlide;
}
public boolean isCanSlide() {
return canSlide;
}
public void setDecorView(View decorView) {
this.decorView = decorView;
}
/***
* 当Activity完成create是调用
* 初始化拖动布局
* 思路:首先获取当前acitivity的rootView
* 获取其下第一个布局后,将此布局首先在rootview中删除,以便添加至此布局中,做完其唯一子布局。
* 然后将此布局添加至rootview中,完成布局的初始化
*/
public void intiContentViewByAcitivity(SlideBaseActivity activity)
{
//设置布局layoutParams
ViewGroup.LayoutParams layoutParams=new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
setLayoutParams(layoutParams);
//指定当前包裹的内容来自于activity
mActivity=activity;
ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
View view = decorView.getChildAt(0);
view.setBackgroundColor(Color.WHITE);//默认给个白色
// You must call removeView() on the child's parent first.
decorView.removeView(view);
addView(view);//将子布局添加至Layout中
decorView.addView(this);
setDecorView(view);
}
/**
* 初始化操作
*/
private void inti()
{
callBack=new ViewDragHelperCallBack(this);
dragHelper=ViewDragHelper.create(this,DEFAULT_SENSITIVITY,callBack);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if(isCanSlide()) {
return dragHelper.shouldInterceptTouchEvent(ev);
}else
{
return false;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(isCanSlide()) {
dragHelper.processTouchEvent(event);
}else
{
return false;
}
return true;
}
/***
* 完成滑动重绘页面
*/
@Override
public void computeScroll() {
if (dragHelper.continueSettling(true)) {
invalidate();
}
}
/***
* 当滑动完成以后回调此函数
*/
@Override
public void onFinish() {
}
}
2.对BaseActivity进行逻辑添加
public class SlideBaseActivity extends AppCompatActivity {
private SlideBackLayout mSlideBackLayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSlideBackLayout=new SlideBackLayout(this);
//设置window为透明
if (mSlideBackLayout.isCanSlide()) {
getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
getWindow().getDecorView().setBackgroundColor(Color.TRANSPARENT);
}
}
/***
* 当Activitity完成后调用
* @param savedInstanceState
*/
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
if (mSlideBackLayout.isCanSlide())
{
//开始初始化SlideBackLayout包裹内容区,将内容区Viewgroup移植至SlideBackLayout
mSlideBackLayout.intiContentViewByAcitivity(this);
}
}
}
到此我们已经完成将主内容区布局添加了一层包裹布局,实现的原理是当该Activity完成渲染好,将整个Activity的decorView拿出来,添加至包裹布局当中,使其成为包裹布局中的顶层子view,然后再把这个包裹好的布局扔回当前Activity的decorView,这样就完成了一个包裹。
第三步 编写ViewDragHelperCallBack类,完成初步逻辑实现顶层Activity可拖动
1.添加Activity管理类
public class ActivityStack implements Application.ActivityLifecycleCallbacks {
private static ActivityStack ourInstance = new ActivityStack();
private Vector<Activity> activities = new Vector<>();
public static ActivityStack getInstance() {
return ourInstance;
}
public ActivityStack() {
}
public Vector<Activity> getActivities() {
return activities;
}
public Activity getTopActivity() {
if (activities.size() > 0) {
return activities.get(activities.size() - 1);
} else {
return null;
}
}
public Activity getBackActivity() {
if (activities.size() > 1) {
return activities.get(activities.size() - 2);
} else {
return null;
}
}
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
int index = activities.indexOf(activity);
if (index == -1) {
activities.add(activity);
}
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
activities.remove(activity);
}
}
public class AppApplication extends Application {
public static AppApplication instance;
//这是库中提供的activity栈,放到Application中,不会被GC
public ActivityStack stack;
@Override
public void onCreate() {
super.onCreate();
instance = this;
stack =ActivityStack.getInstance();
this.registerActivityLifecycleCallbacks(stack);
}
public static AppApplication getInstance() {
return instance;
}
public ActivityStack getStack() {
return stack;
}
}
这个其实是利用了registerActivityLifecycleCallbacks这个机制,完成的一个管理类,不是啥新鲜东西,就不在这里过多讲解了。Activity管理类的作用其实是用来获取相邻两层Activity(用来实现联动效果)以及判断当前是否为做底层Activity(底层Activity不允许滑动)。
public class ViewDragHelperCallBack extends ViewDragHelper.Callback {
private ViewDragLisListener mListener;
private View decorView;
private ViewDragHelper mViewDragHelper;
public ViewDragHelperCallBack(ViewDragLisListener mListener) {
this.mListener = mListener;
}
public void setDecorView(View decorView) {
this.decorView = decorView;
}
public void setViewDragHelper(ViewDragHelper mViewDragHelper) {
this.mViewDragHelper = mViewDragHelper;
}
/***
* 在这里做一些是否允许移动的判断
* @param child
* @param pointerId
* @return
*/
@Override
public boolean tryCaptureView(View child, int pointerId) {
//当应用的Activity数量为1时,此Activity为底层Activity不允许滑动
if(ActivityStack.getInstance().getActivities().size()==1)
{
return false;
}
return true;
}
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//这里应用了一个技巧,在这里的作用是用于判断当前的滑动距离是否要超出child的宽度,超出了就返回0,一直使其做0的偏移
//不做这个处理的话,你会发现你移动的页面可以手动左右的滑出屏幕,比较尴尬
return Math.min(child.getWidth(), Math.max(left, 0));
}
public interface ViewDragLisListener
{
void onFinish();
}
}
ok,简简单单重写了两个方法,现在就已经可以完成顶层页面的左拖动了。