美文网首页学习区干货区Android
Android UI框架快速搭建实践

Android UI框架快速搭建实践

作者: 轻云时解被占用了 | 来源:发表于2016-07-31 09:25 被阅读5626次

    转载请注明原作者,如果你觉得这篇文章对你有帮助或启发,可以关注打赏。

    ui框架搭建.gif

    如上图,本文主要讲解2点 (mvp和dagger2不是本文重点):

    • 基类的抽取和封装(mvp+Dagger2)
    • 如何使用ViewPager+TabLayout快速搭建ios风格的多个底部导航栏的主页框架

    别问我为什么不来个MaterialDesign风格的,说起来都是泪,我个人是喜欢MD的,可以给用户更清爽更有层次的视觉感受,但致命问题就是她直接呈现给用户的信息少了,与当前国内的关注点有冲突,很多产品不懂MD,Android的界面开发也全按IOS来的,设计图都是一套这种事我是不会告诉你的。

    代码最直观,我就直接上代码了。ps:为节省大家时间,本文只会展示核心代码。

    BaseActivity

     package com.example.arron.demo.view.base;
     import android.support.v7.app.AppCompatActivity;
     import android.view.KeyEvent;
     import com.example.arron.demo.internal.di.modules.ActivityModule;
     import com.example.arron.demo.view.navigation.Navigator;
     import javax.inject.Inject;
     /**
     * Created by Arron on 16/6/28.
     */
      public abstract class BaseActivity extends AppCompatActivity {
        //使用Dagger2注入的全局导航类
        @Inject
        public Navigator navigator;
        //动态获取类名 打印日志使用
        protected String TAG = this.getClass().getSimpleName();
    
        //布局文件ID
        protected abstract int getContentViewId();
    
        /**
         * 布局中Fragment的ID
         * 如果没有fragment则不必实现
         */
        protected abstract int getFragmentContentId();
    
        //添加fragment
        protected void addFragment(BaseFragment fragment) {
            if (fragment != null) {
                getSupportFragmentManager().beginTransaction()
                        .replace(getFragmentContentId(), fragment, fragment.getClass().getSimpleName())
                        .addToBackStack(fragment.getClass().getSimpleName())
                        .commitAllowingStateLoss();
            }
        }
    
        //移除fragment
        protected void removeFragment() {
            if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
                getSupportFragmentManager().popBackStack();
            } else {
                finish();
            }
        }
    
        //返回键返回事件的处理
        //如果FragmentStack中只有1个fragment 关闭当前activity
        // 如果FragmentStack中还有>1数量fragment则可以removeFragment()将fragment出栈 此部分交给子类实现
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event) {
            if (KeyEvent.KEYCODE_BACK == keyCode) {
                if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
                    finish();
                    return true;
                }
            }
            return super.onKeyDown(keyCode, event);
        }
    
        @Override
        protected void onStop() {
            super.onStop();
        }
    
        //配合Dagger2使用 返回当前Activity的ActivityModule对象
        // ActivityModule生命周期与activity是绑定的
        protected ActivityModule getActivityModule() {
            return new ActivityModule(this);
        }
    }
    

    AppActivity

    package com.example.arron.demo.view.base;
    
    import android.content.Intent;
    import android.os.Bundle;
    
    import com.example.arron.demo.AndroidApplication;
    
    /**
     * Created by Arron on 16/6/29.
     */
    
    public abstract class AppActivity extends BaseActivity {
    
        /**
         * 获取第一个fragment  如果没有返回null即可
         */
        protected abstract BaseFragment getFirstFragment();
    
        /**
         * 处理Intent
         *
         * @param intent
         */
        protected void handleIntent(Intent intent) {
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(getContentViewId());
            AndroidApplication.getComponent().inject(this);
            if (null != getIntent()) {
                handleIntent(getIntent());
            }
            initView();
            initData();
            //避免重复添加Fragment
            if (null == getSupportFragmentManager().getFragments()) {
                BaseFragment firstFragment = getFirstFragment();
                if (null != firstFragment) {
                    addFragment(firstFragment);
                }
            }
        }
    
        /**
         * 初始化data
         */
        protected abstract void initData();
    
        /**
         * 初始化view
         */
        protected abstract void initView();
    
    }
    

    HomeActivity

    package com.example.arron.demo.view.activity;
    
    import android.graphics.drawable.Drawable;
    import android.support.design.widget.TabLayout;
    import android.support.v4.view.ViewPager;
    import android.view.LayoutInflater;
    import android.widget.TextView;
    
    import com.example.arron.demo.R;
    import com.example.arron.demo.utils.ResourceUtils;
    import com.example.arron.demo.view.adapter.HomeFragmentAdapter;
    import com.example.arron.demo.view.base.AppActivity;
    import com.example.arron.demo.view.base.BaseFragment;
    
    import butterknife.Bind;
    import butterknife.ButterKnife;
    
    /**
     * Created by Arron on 16/6/29.
     */
    
    public class HomeActivity extends AppActivity {
        @Bind(R.id.home_content)
        ViewPager container;
        @Bind(R.id.tab)
        TabLayout tab;
        private HomeFragmentAdapter adapter;
    
        @Override
        protected BaseFragment getFirstFragment() {
            return null;
        }
    
        @Override
        protected void initData() {
    
        }
    
        @Override
        protected void initView() {
            ButterKnife.bind(this);
            tab.setTabMode(TabLayout.MODE_FIXED);
            initTab();
            setListener();
            setAdapterAndNotify();
            container.setOffscreenPageLimit(3);
        }
    
        private void setAdapterAndNotify() {
            if (null == adapter) {
                adapter = new HomeFragmentAdapter(getSupportFragmentManager(), 4);
                container.setAdapter(adapter);
            } else {
                adapter.notifyDataSetChanged();
            }
        }
    
        private void setListener() {
            //这行代码将TabLayout与ViewPager的页面切换绑定 原理很简单 看源码
            container.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tab));
            tab.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    int position = tab.getPosition();
                    //ViewPager切换页面无动画需要使用两个参数的方法并传入false
                    container.setCurrentItem(position, false);
                    //这句别忘了 否则tab就丢失选择器效果了
                    tab.getCustomView().setEnabled(true);
                    //当前页面的数据加载
                    adapter.getItem(position).loadData();
                }
    
                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                    //别忘了
                    tab.getCustomView().setEnabled(false);
                }
    
                @Override
                public void onTabReselected(TabLayout.Tab tab) {
    
                }
            });
    
        }
    
        //为了达到切换tab文字和icon同步变色 这里给TextView设置选择器使用Enabled属性切换
        //icon同理
        private void initTab() {
            LayoutInflater inflater = getLayoutInflater();
            TextView view;
            for (int i = 0; i < 4; i++) {
                view = (TextView) inflater.inflate(R.layout.tab_home_item, null);
                String text = null;
                Drawable drawable = null;
                switch (i) {
                    case 0:
                        text = ResourceUtils.getString(R.string.tab_main);
                        view.setEnabled(true);
                        drawable = ResourceUtils.getDrawable(R.drawable.tab_main);
                        break;
                    case 1:
                        text = ResourceUtils.getString(R.string.tab_what);
                        drawable = ResourceUtils.getDrawable(R.drawable.tab_what);
                        break;
                    case 2:
                        text = ResourceUtils.getString(R.string.tab_message);
                        drawable = ResourceUtils.getDrawable(R.drawable.tab_message);
                        break;
                    case 3:
                        text = ResourceUtils.getString(R.string.tab_mine);
                        drawable = ResourceUtils.getDrawable(R.drawable.tab_mine);
                        break;
                }
                view.setText(text);
                drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
                view.setCompoundDrawables(null, drawable, null, null);
                TabLayout.Tab tab = this.tab.newTab().setCustomView(view);
                this.tab.addTab(tab, i == 0 ? true : false);
            }
        }
    
        @Override
        protected int getContentViewId() {
            return R.layout.activity_home;
        }
    
        @Override
        protected int getFragmentContentId() {
            return 0;
        }
    }
    

    BaseFragment

    package com.example.arron.demo.view.base;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.example.arron.demo.presenter.Presenter;
    import com.example.arron.demo.view.BaseView;
    import com.example.arron.demo.view.loading.VaryViewHelperController;
    import com.trello.rxlifecycle.components.support.RxFragment;
    
    import java.util.List;
    
    import butterknife.ButterKnife;
    
    
    /**
     * Created by Arron on 16/6/29.
     */
    
    public abstract class BaseFragment<T extends Presenter> extends RxFragment implements BaseView {
        //与Fragment绑定的activity对象
        protected BaseActivity mActivity;
        //当前View的Presenter
        protected T mPresenter;
        private View contentView;
        //通用loading页error页等的控制器
        private VaryViewHelperController mVaryViewHelperController;
    
        protected abstract void initView(View view, Bundle savedInstanceState);
    
        /**
         * 初始化数据 页面加载完毕调用
         */
        protected abstract void initData();
    
        /**
         * 切换到页面需要重新加载数据的实现此方法
         */
        public abstract void loadData();
    
        //获取布局文件ID
        protected abstract int getLayoutId();
    
        //获取宿主Activity
        protected BaseActivity getHoldingActivity() {
            return mActivity;
        }
    
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            this.mActivity = (BaseActivity) activity;
        }
    
        //添加fragment
        protected void addFragment(BaseFragment fragment) {
            if (null != fragment) {
                getChildFragmentManager().beginTransaction()
                        .replace(getFragmentContentId(), fragment, fragment.getClass().getSimpleName())
                        .addToBackStack(fragment.getClass().getSimpleName())
                        .commitAllowingStateLoss();
            }
        }
    
        //移除fragment
        protected void removeFragment() {
            if (getChildFragmentManager().getBackStackEntryCount() > 1) {
                getChildFragmentManager().popBackStack();
            }
        }
    
        //添加fragment的布局节点的ID
        protected abstract int getFragmentContentId();
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            if (contentView == null) {
                contentView = inflater.inflate(getLayoutId(), container, false);
                initView(contentView, savedInstanceState);
            } else {
                ViewGroup parent = (ViewGroup) contentView.getParent();
                if (parent != null) {
                    parent.removeView(contentView);
                }
            }
            if (null == mVaryViewHelperController)
                mVaryViewHelperController = new VaryViewHelperController(getLoaingTargetView());
            if (null == mPresenter)
                mPresenter = getChildPresenter();
            return contentView;
        }
    
        protected abstract T getChildPresenter();
    
        protected abstract View getLoaingTargetView();
    
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            initData();
        }
    
        @Override
        public void setMenuVisibility(boolean menuVisible) {
            super.setMenuVisibility(menuVisible);
            if (null != this.getView()) {
                this.getView().setVisibility(menuVisible ? View.VISIBLE : View.INVISIBLE);
            }
        }
    
        @Override
        public void onResume() {
            super.onResume();
            if (null != mPresenter)
                mPresenter.resume();
        }
    
        @Override
        public void onPause() {
            super.onPause();
            if (null != mPresenter)
                mPresenter.pause();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            ButterKnife.unbind(this);
            if (null != mPresenter)
                mPresenter.destroy();
        }
    
        @Override
        public BaseActivity getContext() {
            return mActivity;
        }
    
        @Override
        public void onSaveInstanceState(Bundle outState) {
            //Google bug
            outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
            super.onSaveInstanceState(outState);
        }
    
        @Override
        public void showLoading() {
            if (mVaryViewHelperController == null) {
                throw new IllegalStateException("no ViewHelperController");
            }
            mVaryViewHelperController.showLoading();
        }
    
        @Override
        public void refreshView() {
            if (mVaryViewHelperController == null) {
                throw new IllegalStateException("no ViewHelperController");
            }
            mVaryViewHelperController.restore();
        }
    
        @Override
        public void showNetError() {
            if (mVaryViewHelperController == null) {
                throw new IllegalStateException("no ViewHelperController");
            }
            mVaryViewHelperController.showNetworkError(v -> {
                showLoading();
                mPresenter.requestData(getRequestParams());
            });
        }
    
        @Override
        public void hasNoMoreData() {
    
        }
    
        @Override
        public void loadMoreFinish(List dates) {
    
        }
    
        @Override
        public void showRefreshFinish(List score) {
    
        }
    
        @Override
        public void showToastError() {
    
        }
    
        protected String getRequestParams() {
            return null;
        }
    
        @Override
        public void showEmptyView(String msg) {
            if (mVaryViewHelperController == null) {
                throw new IllegalStateException("no ViewHelperController");
            }
            mVaryViewHelperController.showEmpty(msg);
        }
    }
    

    eg:具体页面的Fragment

    package com.example.arron.demo.view.fragment;
    
    import android.os.Bundle;
    import android.view.View;
    
    import com.example.arron.demo.R;
    import com.example.arron.demo.presenter.Presenter;
    import com.example.arron.demo.view.base.BaseFragment;
    
    /**
     * Created by Arron on 16/6/29.
     */
    public class MineFragment extends BaseFragment {
    
        @Override
        protected void initView(View view, Bundle savedInstanceState) {
    
        }
    
        @Override
        protected void initData() {
    
        }
    
        @Override
        public void loadData() {
    
        }
    
        @Override
        protected int getLayoutId() {
            return R.layout.fragment_mine;
        }
    
        @Override
        protected int getFragmentContentId() {
            return 0;
        }
    
        @Override
        protected Presenter getChildPresenter() {
            return null;
        }
    
        @Override
        protected View getLoaingTargetView() {
            return null;
        }
    }
    

    View层使用到的VaryViewHelperController

    package com.example.arron.demo.view.loading;
    
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    
    import com.example.arron.demo.R;
    
    
    public class VaryViewHelperController {
    
        private IVaryViewHelper helper;
    
        public VaryViewHelperController(View view) {
            this(new VaryViewHelper(view));
        }
    
        public VaryViewHelperController(IVaryViewHelper helper) {
            super();
            this.helper = helper;
        }
    
        public void showNetworkError(View.OnClickListener onClickListener) {
            View layout = helper.inflate(R.layout.pager_error);
            Button againBtn = (Button) layout.findViewById(R.id.pager_error_loadingAgain);
            if (null != onClickListener) {
                againBtn.setOnClickListener(onClickListener);
            }
            helper.showLayout(layout);
        }
    
        public void showEmpty(String emptyMsg) {
            View layout = helper.inflate(R.layout.page_no_data);
            TextView textView = (TextView) layout.findViewById(R.id.tv_no_data);
            if (!TextUtils.isEmpty(emptyMsg)) {
                textView.setText(emptyMsg);
            }
            helper.showLayout(layout);
        }
    
        public void showLoading() {
            View layout = helper.inflate(R.layout.pager_loading);
            helper.showLayout(layout);
        }
    
        public void restore() {
            helper.restoreView();
        }
    }
    

    VaryViewHelperController中使用到的添加移除view的工具类IVaryViewHelper

    package com.example.arron.demo.view.loading;
    
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    /**
     * VaryViewHelper可以方便添加或移除view
     */
    public class VaryViewHelper implements IVaryViewHelper {
        private View view;
        private ViewGroup parentView;
        private int viewIndex;
        private ViewGroup.LayoutParams params;
        private View currentView;
    
        public VaryViewHelper(View view) {
            super();
            this.view = view;
        }
    
        private void init() {
            params = view.getLayoutParams();
            if (view.getParent() != null) {
                parentView = (ViewGroup) view.getParent();
            } else {
                parentView = (ViewGroup) view.getRootView().findViewById(android.R.id.content);
            }
            int count = parentView.getChildCount();
            for (int index = 0; index < count; index++) {
                if (view == parentView.getChildAt(index)) {
                    viewIndex = index;
                    break;
                }
            }
            currentView = view;
        }
    
        @Override
        public View getCurrentLayout() {
            return currentView;
        }
    
        @Override
        public void restoreView() {
            showLayout(view);
        }
    
        @Override
        public void showLayout(View view) {
            if (parentView == null) {
                init();
            }
            this.currentView = view;
            if (parentView.getChildAt(viewIndex) != view) {
                ViewGroup parent = (ViewGroup) view.getParent();
                if (parent != null) {
                    parent.removeView(view);
                }
                parentView.removeViewAt(viewIndex);
                parentView.addView(view, viewIndex, params);
            }
        }
    
        @Override
        public View inflate(int layoutId) {
            return LayoutInflater.from(view.getContext()).inflate(layoutId, null);
        }
    
        @Override
        public Context getContext() {
            return view.getContext();
        }
    
        @Override
        public View getView() {
            return view;
        }
    }
    

    That's all!我个人更喜欢看代码,所以没有大段的文字,但关键点都在代码中注释了,如果有不明白的地方,欢迎提问。

    应大家的要求将代码分享到GitHub上了,感兴趣的同学可以去看看。

    相关文章

      网友评论

      • 03ef7e1c4d3e:代码不完整 能放一份完整的吗
      • iot_xc:楼主,你的这个项目里少了DaggerApplicationComponent文件,麻烦你能传一下吗,感谢
        六月L:Dagger开头的都是生成的,编译一下项目就有了
      • leon2017:你用tablayout设计首页的话,如果业务逻辑复杂的话,需要跳转首页指定的fragment的话,处理比较麻烦
      • 平凡而简单:TabLayout中的的自定义View不可以使用selector选择器吗??
        看到有帖子说可以menu菜单xml配置实现,楼主是否用过!
        平凡而简单: @Metal626 一个意思,就是菜单文件配置!
        轻云时解被占用了: @平凡而简单 我用的就是选择器
        不知你所说的menu菜单指的是?
        ToolBar和NavigationView可以使用menu xml配置
      • 606fd5f5448c:感谢分享
      • 我是云峰小罗:好文,很干活
      • 方小钢:感谢楼主分享
      • 轻云时解被占用了:已添加GitHub地址,欢迎交流。
      • RicardoMJiang:同期待Github
        轻云时解被占用了:@whuhan2013 文末已添加GitHub地址,欢迎交流。
      • longzekai:期待有GitHub的地址。
        轻云时解被占用了:@longzekai 文末已添加GitHub地址,欢迎交流。
      • dongjunkun:返回键的监听直接使用onBackPressed方法就可以了
        轻云时解被占用了:@dongjunkun 都行 onBackPressed()也是在activity的onKeyDown()调用的
      • The_Coder:建议在 GitHub 有个完整的示例会更好。
        轻云时解被占用了:@叫我序员就好 好的 我会尽快整理出来 提交到github上。
      • i卓:一直想这么弄,自己在项目中虽然也有这种用法,但是都是简单零碎的封装,非常感谢作者的分享,赞!!!

      本文标题:Android UI框架快速搭建实践

      本文链接:https://www.haomeiwen.com/subject/bhctsttx.html