本项目来源于慕课网Android实战课——Android通用框架设计与完整电商开发
前天跟随视频完成了对主界面的封装及使用,这种模式使用的还不够熟悉,需加强理解并练习。写下这篇来加深理解。
先上效果图:
哈哈,别想多,就是我们平常见的效果。底部一些Tab,上部是与之对应的Fragment。但是与以往不同的是,该界面本身还是一个Fragment,但都是一个原理(底部导航+Fragment)。
在项目中是进行抽取(主界面的基类,详情页的基类)。主界面分为两个部分,上部详情页的容器,下部Tab导航栏,同时对Tab导航栏的点击事件进行处理。详情页的基类比较简单,封装了一些通用的功能:如双击返回键退出应用等。
首先上详情页的代码:
public abstract class ButtomItemdelegate extends MinDelegate implements View.OnKeyListener {
private long mExitTime = 0;
private static final int EXIT_TIME = 2000;
@Override
public void onResume() {
super.onResume();
View rootView = getView();
if (rootView != null) {
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener(this);
}
}
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if (System.currentTimeMillis() - mExitTime > EXIT_TIME) {
Toast.makeText(getContext(), "双击退出应用", Toast.LENGTH_SHORT).show();
mExitTime = System.currentTimeMillis();
} else {
_mActivity.finish();
if (mExitTime != 0) {
mExitTime = 0;
}
}
return true;
}
return false;
}
}
这里代码来说相对简单,就提取了一个功能,就是双击返回键退出应用。
监听返回键的点击事件,判断两次点击的间隔时间,如果大于2s,就提示,小于2s直接退出。值得注意的是,在Fragment中,每次重新进入都要对View的焦点重新获取,并重新注册点击事件。代码如下:
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener(this);
接着是每个Tab的信息,建一个类:
public class ButtomTabBean {
private final CharSequence ICON;
private final CharSequence TITLE;
public ButtomTabBean(CharSequence icon, CharSequence title) {
ICON = icon;
TITLE = title;
}
public CharSequence getIcon() {
return ICON;
}
public CharSequence getTitle() {
return TITLE;
}
}
ICON, TITLE,表示每个Tab有一个图标,和个描述文字。
public class ItemBuilder {
private final LinkedHashMap ITEMS = new LinkedHashMap<>();
static ItemBuilder builder() {
return new ItemBuilder();
}
public final ItemBuilder addItem(ButtomTabBean bean, ButtomItemdelegate itemdelegate) {
ITEMS.put(bean, itemdelegate);
return this;
}
public final ItemBuilder addItems(LinkedHashMap items) {
ITEMS.putAll(items);
return this;
}
public final LinkedHashMap build() {
return ITEMS;
}
}
ItemBuilder是将Tab与界面关联起来。
然后就是主界面的基类,代码:
public abstract class BaseButtomDelegete extends MinDelegate implements View.OnClickListener {
//保存Tab的内容
private final ArrayList TAB_BEANS = new ArrayList<>();
//保存每个Tab所对应的Fragment
private final ArrayList TAB_DELEGATES = new ArrayList<>();
private final LinkedHashMap ITEMS = new LinkedHashMap<>();
private int mCurrentItem = 0; //当前界面
private int mIndexItem = 0; //进入的第一个界面
private int mTabColor = Color.RED;
@BindView(R2.id.mBottomBar)
LinearLayoutCompat mBottomBar;
public abstract LinkedHashMap setItems(ItemBuilder builder);
/**
* @return 进入应用显示的界面
*/
public abstract int setIndexItem();
/**
* @return tab点击后的颜色
*/
@ColorInt
public abstract int setColor();
@Override
public Object setLayout() {
return R.layout.buttom_delegate_layout;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mIndexItem = setIndexItem();
if (setColor() != 0) {
mTabColor = setColor();
}
final ItemBuilder builder = new ItemBuilder();
final LinkedHashMap maps = setItems(builder);
ITEMS.putAll(maps);
for (Map.Entry item : ITEMS.entrySet()) {
final ButtomTabBean key = item.getKey();
final ButtomItemdelegate value = item.getValue();
TAB_BEANS.add(key);
TAB_DELEGATES.add(value);
}
}
@Override
public void onBindView(@Nullable Bundle savedInstanceState, View rootView) {
final int size = ITEMS.size();
for (int i = 0; i < size; i++) {
LayoutInflater.from(getContext()).inflate(R.layout.buttom_item_icon_title_layout, mBottomBar);
final RelativeLayout item = (RelativeLayout) mBottomBar.getChildAt(i);
//设置每个tab的点击事件
item.setTag(i);
item.setOnClickListener(this);
final IconTextView icon = (IconTextView) item.getChildAt(0);
final IconTextView tv = (IconTextView) item.getChildAt(1);
final ButtomTabBean bean = TAB_BEANS.get(i);
//初始化数据
icon.setText(bean.getIcon());
tv.setText(bean.getTitle());
if (i == mIndexItem) {
icon.setTextColor(mTabColor);
tv.setTextColor(mTabColor);
}
}
//初始化每个TAB对应的界面
final ISupportFragment[] fragments = TAB_DELEGATES.toArray(new ISupportFragment[size]);
getSupportDelegate().loadMultipleRootFragment(R.id.buttom_frame, mIndexItem, fragments);
}
private void resetColor() {
final int count = mBottomBar.getChildCount();
for (int i = 0; i < count; i++) {
final RelativeLayout item = (RelativeLayout) mBottomBar.getChildAt(i);
final IconTextView itemIcon = (IconTextView) item.getChildAt(0);
itemIcon.setTextColor(Color.GRAY);
final IconTextView itemTitle = (IconTextView) item.getChildAt(1);
itemTitle.setTextColor(Color.GRAY);
}
}
@Override
public void onClick(View v) {
final int tag = (int) v.getTag();
resetColor();
final RelativeLayout item = (RelativeLayout) v;
final IconTextView itemIcon = (IconTextView) item.getChildAt(0);
itemIcon.setTextColor(mTabColor);
final IconTextView itemTitle = (IconTextView) item.getChildAt(1);
itemTitle.setTextColor(mTabColor);
//展示和隐藏Fragment 参数一为要展示的 参数二为要隐藏的
getSupportDelegate().showHideFragment(TAB_DELEGATES.get(tag), TAB_DELEGATES.get(mCurrentItem));
//注意先后顺序
mCurrentItem = tag;
}
}
首先声明的几个参数:
ArrayList
:保存Tab所对应的内容。
ArrayList
:保存Tab所对应的Fragmnet。
private final LinkedHashMap
:使每个ButtomTabBean对象与对应的Fragment对应起来。
还有几个抽象方法:
public abstract LinkedHashMap setItems(ItemBuilder builder); //创建Tab类与Fragment类,并传入到界面
public abstract int setIndexItem(); //设置第一次进入的要显示第几个界面
public abstract int setColor(); //设置Tab点击后的颜色
然后就是主代码,在oncreat中将 Tab与绑定的界面获取出来,保存到TAB_BEANS,TAB_DELEGATES中,这样就可以控制。
接着在onBindView中对视图进行绑定,循环依次取出每个TabBean,然后使用LayoutInflater加载每个Tab视图,并添加到容器中,这个容器就是界面底部的一个LinearLayout,同时从TAB_BEANS中获取数据,并赋值。这里记得对每个Tab的点击事件进行注册。点击事件主要处理的就是Tab显示及对应界面变化。
//初始化每个TAB对应的界面
final ISupportFragment[] fragments = TAB_DELEGATES.toArray(new ISupportFragment[size]);
getSupportDelegate().loadMultipleRootFragment(R.id.buttom_frame, mIndexItem, fragments);
这两句代码实现的对界面(Fragment)的加载,是框架Fragmentation里的内容,不太懂,待我研究下。
到这里,这基类的抽取就算完成了。然后就是使用:
public class Bottomdalegate extends BaseButtomDelegete {
@Override
public LinkedHashMap setItems(ItemBuilder builder) {
final LinkedHashMap ITEMS=new LinkedHashMap<>();
ITEMS.put(new ButtomTabBean("{fa-home}","主页"),new IndexDelegate());
ITEMS.put(new ButtomTabBean("{fa-sort}","分类"),new SortDelegate());
ITEMS.put(new ButtomTabBean("{fa-compass}","发现"),new CompassDelegate());
ITEMS.put(new ButtomTabBean("{fa-shopping-cart}","购物车"),new ShopCarDelegate());
ITEMS.put(new ButtomTabBean("{fa-user}","我的"),new UserDelegate());
return builder.addItems(ITEMS).build();
}
@Override
public int setIndexItem() {
return 0;
}
@Override
public int setColor() {
return Color.parseColor("#0099cc");
}
}
前面封装好了,这里代码就比较简单了,直接实现三个抽象方法。
到这里,主界面就基本实现了。可以使用这种思想,不用每次对视图直接创建,而是通过动态绑定的方式传入数据,这样就比较灵活。