快速开发之加载动画

来源:互联网 发布:单反照片导入mac 编辑:程序博客网 时间:2024/06/05 15:21

现在的App或多或少都会用到加载动画,那么如何将多样性的加载动画集成到我们的页面框架中呢?

一些实现方式:

方式一:将加载动画封装成自定义View,在布局中进行添加。

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/activity_project_select_designer"    android:layout_width="match_parent"    android:layout_height="match_parent">    <android.support.v7.widget.RecyclerView            android:id="@+id/recycler_view"            android:layout_width="match_parent"            android:layout_height="match_parent"/>    <LoadingProgress        android:id="@+id/loading_progress"        android:layout_width="match_parent"        android:layout_height="match_parent" /></FrameLayout>

这样的实现方式,需要在每一个使用加载动画的布局文件中添加LoadingProgress,并且在界面调用时控制显示逻辑。如果大面积的使用到重复的代码,我们可以对代码进行封装,例如封装成一个RecyclerWithLoadAnimView,在该类中进行对LoadingProgress的显示控制。

但是问题来了,如果有特定需求需要继承RecyclerView实现特定功能呢?

所以我们还是不这样写了,为了解耦合,我们还是讲LoadingProgress放在别的地方去写。

方式二:在BaseActivity中添加LoadingProgress

那么问题来了,我们怎么在不通过布局文件,并且不影响子类的情况下,添加LoadingProgress呢?
其实很简单,DecorView大家都知道吧,这是我们在onCreate() 的setContentView()方法返回的布局的一个父容器。我们可以直接将LoadingProgress添加到布局的一个父容器中就可以了,然后在BaseActivity中对LoadingProgress进行逻辑控制。

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(...);    }

现在我们找一下这个DecorView。大家都知道,我们视图都会放在Window里,而Window类却是一个抽象类,所以我们要找到它的子类,PhoneWindow类,就是这个类,我们的setContentView方法最终会调用PhoneWindow类中的

@Overridepublic void setContentView(int layoutResID) {        if (mContentParent == null) {            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        ...    }

多余的代码我们不用看,我们只需要看installDecor()方法,这就是创建DecorView的方,我们看一下:

private void installDecor() {        if (mDecor == null) {            mDecor = generateDecor();            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);            mDecor.setIsRootNamespace(true);            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);            }        }        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);        ...        }}protected DecorView generateDecor() {        return new DecorView(getContext(), -1);}private final class DecorView extends FrameLayout{    ...}

这里我们看到generateDecor()方法,这只是创建了一个DecorView,不过我们可以知道DecorView是FrameLayout的子类。
但是我们没有找到对我们有利的东西,不过大家不要气馁,相信有些人已经看到另一个对象mContentParent,顾名思义,它应该是内容的父类,我们继续往下看 mContentParent = generateLayout(mDecor); 这个MContentParent是通过mDecor创建的。接下来,我们看一下generateLayout()方法:

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;protected ViewGroup generateLayout(DecorView decor) {    ...    View in = mLayoutInflater.inflate(layoutResource, null);    decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));    mContentRoot = (ViewGroup) in;    ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);    ...    return contentParent;}

通过对源码的查看,我们知道了mContentParent是DecorView 的一个子View,而contentParent是通过findViewById()这个方法得到的,而这个Id就是 R.id.content ,所以我们找到了页面的另一父容器mContentParent,并获取到了它在布局中的Id。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="true"    android:orientation="vertical">    <ViewStub android:id="@+id/action_mode_bar_stub"              android:inflatedId="@+id/action_mode_bar"              android:layout="@layout/action_mode_bar"              android:layout_width="match_parent"              android:layout_height="wrap_content"              android:theme="?attr/actionBarTheme" />    <FrameLayout         android:id="@android:id/content"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:foregroundInsidePadding="false"         android:foregroundGravity="fill_horizontal|top"         android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>

这样我们在BaseActivity中就能得到mContentParent对象了。而且它也是一个帧布局,讲到这里应该比较清楚了,我们可以直接将LoadingProgress添加到mContentParent中,并在BaseActivity中进行逻辑控制。

public abstract class BaseActivity extends AppCompatActivity {    private LoadingProgress mProgress;    private List<View> views = new ArrayList<>();    private boolean isLoading = false;    private ViewGroup mContentView;    @Override    public void onPostCreate(Bundle savedInstanceState) {        super.onPostCreate(savedInstanceState);        initView();        initData();        initListener();    }    @Override    public void setContentView(int layoutResID) {        super.setContentView(layoutResID);        //在setContentView() 中所设置布局的父容器的ID 是 android.R.id.content        mContentView = (ViewGroup) findViewById(android.R.id.content);        setupLoadView();    }    /**     * 初始化加载动画视图     * 找到布局中的所有一级子view     */    private void setupLoadView() {        if (mProgress != null)            return;        mProgress = new LoadingProgress(this);        mProgress.setBackgroundResource(R.color.all_bg);        View contentView = mContentView.getChildAt(0);        if (contentView instanceof ViewGroup) {            ViewGroup contentGroup = (ViewGroup) contentView;            for (int i = 1; i < contentGroup.getChildCount(); i++) {                views.add(contentGroup.getChildAt(i));            }        }        int marginTop = getTitleHeight();        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(                ViewGroup.LayoutParams.MATCH_PARENT,                ViewGroup.LayoutParams.MATCH_PARENT);        layoutParams.setMargins(0, marginTop, 0, 0);        mContentView.addView(mProgress, layoutParams);    }    /**     * 开启动画     */    protected void openLoadAnim() {        if (isLoading || mProgress == null)            return;        mProgress.show();        isLoading = true;    }    /**     * 关闭动画     */    protected void closeLoadAnim() {        if (!isLoading || mProgress == null)            return;        mProgress.dismiss();        isLoading = false;    }    /**     * 初始化view     * 设置view 部分属性(显示隐藏等)     */    protected abstract void initView();    /**     * 加载数据     * 在这里调用 openLoadAnim() 方法     */    protected abstract void initData();    /**     * 初始化监听     */    protected abstract void initListener();    /**     * 返回title高度,防止加载动画格挡标题     */    protected abstract int getTitleHeight();    /**     * 父容器获取焦点,禁止子控件自动获取焦点     * 布局中有EditText时,禁止弹出软键盘     */    protected void containerFocus() {        mContentView.getChildAt(0).setFocusable(true);    }    @Override    protected void onDestroy() {        super.onDestroy();        closeLoadAnim();        mProgress = null;        views.clear();        views = null;        mContentView = null;    }}

大家可能看到我没有在onCreate中调用initView(),initData(),initListener()方法,而是在onPostCreate方法中调用。原因就留给大家去探索了。

项目地址

https://github.com/Huang102/LoadAnimTemplate

0 0