快速开发之加载动画

3 篇文章 0 订阅
2 篇文章 0 订阅

现在的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类中的

@Override
public 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值