转载请注明原作者,如果你觉得这篇文章对你有帮助或启发,可以关注打赏。
如上图,本文主要讲解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上了,感兴趣的同学可以去看看。