模仿微信,android bottom navigation的实现
来源:互联网 发布:在iphone编程 编辑:程序博客网 时间:2024/05/23 12:57
微信出现之后,它的底部导航(bottom navigation)引领了潮流,后来,很多软件都进行模仿,纷纷加入了bottom navigation。那么,我也来模仿模仿,虽然与微信的底部效果有点区别,但是,毕竟是个学习的过程,就记录了下来,也给大家参考参考。
先给大家看一下效果:
下面介绍一下实现过程:
ItemView的设计
BottomNavigation有四个ItemView,对应四个TabItemView,TabItemView布局也很简单,如下(tab_item_view.xml):
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:id="@+id/tab_icon" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center_horizontal" /> <TextView android:id="@+id/tab_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:textSize="12sp" /></LinearLayout>定义了一个ImageView和一个TextView。tab_item_view.xml这个布局文件将会在TabItemView这个类中引用。
TabItemView继承了LinearLayout,
public class TabItemView extends LinearLayout{ private ColorStateList colorStateList; private ImageView mTabIcon; private TextView mTabText; public TabItemView(Context context) { this(context, null); } public TabItemView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TabItemView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context); } public void setDefaultTextColor(ColorStateList colorStateList) { this.colorStateList = colorStateList; } private void initView(Context context) { setOrientation(VERTICAL); setGravity(Gravity.CENTER); LayoutInflater.from(context).inflate(R.layout.tab_item_view, this, true); mTabIcon = (ImageView) findViewById(R.id.tab_icon); mTabText = (TextView) findViewById(R.id.tab_text); } public void initData(TabItem tabItem) { if (mTabIcon != null && mTabText!= null && colorStateList != null){ mTabIcon.setImageResource(tabItem.getImageResId()); mTabText.setTextColor(colorStateList); mTabText.setText(tabItem.getTextResId()); } }}在initView方法中获取到布局并获取tabIcon和tabText,initData方法用来初始化tabIcon和tabText,colorStateList是一个ColorStateList对象,这个对象在setDefaultTextColor中被初始化,它定义了selected=true和selected=false两个状态的TextView的颜色。
以上介绍的是BottomNavigation中每个ItemView的创建,接下来定义一个Layout,把这些ItemView放置到其中:
TabLayout的创建:
创建TabLayout继承自LinearLayout,并实现View.OnClickListener接口:
public class TabLayout extends LinearLayout implements View.OnClickListener { private List<TabItem> tabs; private View selectedView; private int tabCount; private ColorStateList colorStateList; private ViewPager mViewPager; public TabLayout(Context context) { this(context, null); } public TabLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TabLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initView(context, attrs); } private void initView(Context context, AttributeSet attrs) { setOrientation(HORIZONTAL); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabLayout); for (int i = 0; i < a.getIndexCount(); i++) { int attr = a.getIndex(i); if (attr == R.styleable.TabLayout_textColorSelector) { colorStateList = a.getColorStateList(attr); } } a.recycle(); } @Override public void onClick(View v) { tabClickListener.onTabClick((TabItem) v.getTag()); } public void intData(List<TabItem> tabItems, OnTabClickListener listener) { this.tabClickListener = listener; this.tabs = tabItems; LayoutParams params = new LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT); params.weight = 1; params.setMargins(12,9,12,10); TabItemView itemView = null; if (tabs != null && tabs.size() > 0) { tabCount = tabs.size(); for (int index = 0; index < tabs.size(); index++) { itemView = new TabItemView(getContext()); itemView.setDefaultTextColor(colorStateList); itemView.setTag(tabs.get(index)); itemView.initData(tabs.get(index)); itemView.setOnClickListener(this); addView(itemView, params); } } else { throw new IllegalArgumentException("tabs can not be empty"); } } public void setCurrentTab(int position) { if (position >= 0 && position < tabCount) { View view = getChildAt(position); if (selectedView != view) { view.setSelected(true); if (selectedView != null) { selectedView.setSelected(false); } selectedView = view; } } } public void setTextColorSelected(ColorStateList colorStateList){ this.colorStateList = colorStateList; } public interface OnTabClickListener { void onTabClick(TabItem tabItem); } OnTabClickListener tabClickListener;}在initView方法中获取属性,这个属性在attrs.xml文件中定义,将会在布局文件使用到这个属性:
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="TabLayout"> <attr name="textColorSelector" format="reference"/> </declare-styleable></resources>在initData()方法中,利用一个循环,初始化所有的TabItemView:
for (int index = 0; index < tabs.size(); index++) { itemView = new TabItemView(getContext()); itemView.setDefaultTextColor(colorStateList); //设置默认的colorStateList itemView.setTag(tabs.get(index)); //设置每个itemView的Tag,便于点击时,利用getTag方法来获取点击的ItemView itemView.initData(tabs.get(index)); //调用TabItemView中的initData方法来初始化每个ItemView itemView.setOnClickListener(this); //初始化监听 addView(itemView, params); //添加View }setCurrentTab()方法用来切换每个TabItemView的selected状态。
定义一个接口OnTabClickListener,返回被点击的TabItemView
到此,一个BottomNavigation已经完成一半了,接下来就是,怎么配合ViewPager来进行页面切换了。
不知道大家有没有遇到这种情况:
如果使用官方提供的ViewPager,当当前页面是第1个页面,然后点击切换到第3个页面的时候,第2个页面还是会被快速显示出来,然后在切换第3个页面。只要所点击的页面和当前页面之间还有其他页面,在点击切换时,都是把中间的所有页面快速切换而过。我不知道我这样描述大家有没有看懂,如果不懂,那我也只能抱歉了。接下来解决这个问题。
自定义ViewPager
解决的思路:当用户滑动页面进行页面切换时,允许ViewPager滑动。如果用户是通过点击底部的Tab来进行页面切换时,禁止ViewPager滑动,直接切换到点击所指的页面。
创建一个IndexViewPager继承自ViewPager
public class IndexViewPager extends ViewPager { private boolean isCanScroll = true; public IndexViewPager(Context context) { super(context); } public IndexViewPager(Context context, AttributeSet attrs) { super(context, attrs); } public void setScanScroll(boolean isCanScroll) { this.isCanScroll = isCanScroll; } @Override public void scrollTo(int x, int y) { super.scrollTo(x, y); } @Override public boolean onTouchEvent(MotionEvent arg0) { // TODO Auto-generated method stub if (isCanScroll) { return super.onTouchEvent(arg0); } else { return false; } } @Override public void setCurrentItem(int item) { // TODO Auto-generated method stub super.setCurrentItem(item, false); } @Override public void setCurrentItem(int item, boolean smoothScroll) { // TODO Auto-generated method stub super.setCurrentItem(item, smoothScroll); } @Override public boolean onInterceptTouchEvent(MotionEvent arg0) { // TODO Auto-generated method stub if (isCanScroll) { return super.onInterceptTouchEvent(arg0); } else { return false; } }}定义一个变量isCanScroll来控制ViewPager是否可以滑动。在onTouchEvent和onInterceptTouchEvent两个方法中添加对isCanScroll的判断,选择性返回。其他两个方法都是默认的。在控制切换ViewPager页面的时候,调用setCanScroll方法就能解决以上所描述的问题了。对于我所提的这个问题,大家可以自行做下实验,增加理解。
实现BottomNavigation:
这里先定义一个Model的来保存每个TabItemView的数据,
在Activity中使用:
public class TabItem { /** * icon */ private int imageResId; /** * the text */ private int textResId; private Class<? extends Fragment> tagFramgentClz; public TabItem(int imageResId, int lableResId) { this.imageResId = imageResId; this.textResId = lableResId; } public TabItem(int imageResId, int lableResId, Class<? extends Fragment> tagFramgentClz) { this.imageResId = imageResId; this.textResId = lableResId; this.tagFramgentClz = tagFramgentClz; } public int getImageResId() { return imageResId; } public void setImageResId(int imageResId) { this.imageResId = imageResId; } public int getTextResId() { return textResId; } public void setTextResId(int textResId) { this.textResId = textResId; } public Class<? extends Fragment> getTagFramgentClz() { return tagFramgentClz; } public void setTagFramgentClz(Class<? extends Fragment> tagFramgentClz) { this.tagFramgentClz = tagFramgentClz; }}
在Activity的布局文件中添加IndexViewPager和TabLayout:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation='vertical' tools:context="com.wujs.bottomnavigationtest.MainActivity"> <com.wujs.bottomnavigation.IndexViewPager android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/tab_layout" /> <com.wujs.bottomnavigation.TabLayout android:id="@+id/tab_layout" android:layout_width="match_parent" android:layout_height="56dp" android:background="@color/white" android:layout_alignParentBottom="true" app:textColorSelector="@drawable/selector_tab_text" /></RelativeLayout>在TabLayout中,定义textColorSelector属性,引用了一个selector资源文件(selector_tab_text.xml):
<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:color="@color/colorPrimary" android:state_selected="true" /> <item android:color="@color/darker_gray" android:state_selected="false" /></selector>
public class MainActivity extends AppCompatActivity implements TabLayout.OnTabClickListener { @BindView(R.id.tab_layout) TabLayout tabLayout; @BindView(R.id.viewPager) IndexViewPager mViewPager; private List<TabItem> tabs; private int selectedPosition; private boolean isPermsGranted = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); initData(); } private void initData() { tabs = new ArrayList<>(); tabs.add(BottomTabPosConstant.HOME_TAB_POS, new TabItem(R.drawable.selector_tab_home_icon, R.string.tabactivity_tab_home_text, PaxFragment.class)); tabs.add(BottomTabPosConstant.CONCERN_TAB_POS, new TabItem(R.drawable.selector_tab_concern_icon, R.string.tabactivity_tab_concern_text, ConcernFragment.class)); tabs.add(BottomTabPosConstant.NEARBY_TAB_POS, new TabItem(R.drawable.selector_tab_nearby_icon, R.string.tabactivty_tab_nearby_text, NearbyFragment.class)); tabs.add(BottomTabPosConstant.ME_TAB_POS, new TabItem(R.drawable.selector_tab_me_icon, R.string.tabactivity_tab_me_text, MeFragment.class)); tabLayout.intData(tabs, this); tabLayout.setCurrentTab(0); FragAdapter adapter = new FragAdapter(getSupportFragmentManager()); mViewPager.setAdapter(adapter); mViewPager.setOffscreenPageLimit(2); //设置限定预加载的ViewPager页面个数 //Change the selected tag, when the viewpager changes page. mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { mViewPager.setScanScroll(true); //enable scroll of viewpager tabLayout.setCurrentTab(position); selectedPosition = position; } @Override public void onPageScrollStateChanged(int state) { } }); } @Override public void onTabClick(TabItem tabItem) { int position = tabs.indexOf(tabItem); if (position != selectedPosition) { mViewPager.setScanScroll(false); //disable scroll of viewpager selectedPosition = position; mViewPager.setCurrentItem(tabs.indexOf(tabItem)); // Here change the selected tag by setting current item of ViewPager. } } private class FragAdapter extends FragmentPagerAdapter { FragAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int arg0) { try { return tabs.get(arg0).getTagFramgentClz().newInstance(); } catch (InstantiationException e) { e.printStackTrace(); return null; } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } @Override public int getCount() { return tabs.size(); } }}
到此,一个模仿微信的BottomNavigation就完成了,欢迎大家来吐槽。
代码已经上传到CSDN上:点击下载BottomNavigationTest
也可以到github上去下载:github
0 0
- 模仿微信,android bottom navigation的实现
- Android Bottom Navigation
- Android学习之微信界面的模仿实现
- #android 利用fragment实现模仿微信的实例
- 使用Bottom Navigation Activity实现Android底部导航栏
- 模仿微信的界面实现
- android模仿微信的链接
- Android Material Bottom Navigation使用教程
- Android Design风格组件之Bottom navigation
- android ActionBar的使用(模仿微信界面)
- 底部导航Bottom navigation的简单使用
- 翻翻git之---实现Material Bottom Navigation的自定义控件 LuseenBottomNavigation
- Android底部bottom的渐变实现
- Android模仿微信拍照对话框
- [模仿Android微信]之主界面
- Android模仿微信语音聊天功能
- android:模仿微信联系人效果
- Android模仿微信加号菜单模式
- 读书笔记 《算法导论》 C2
- 从编译OpenGL库到我的第一个颜色变化的三角形-Shader
- 9-4修改属性的装饰器
- 语言控制台如何设置局部字体和背景的颜色
- 网易OpenStack部署运维实战
- 模仿微信,android bottom navigation的实现
- KVM虚拟化源码分析之KVM_TOOLS(一)
- 加载资源之路径问题
- MYSQL基础(DML)
- 网上找到的最简单说明建立IDoc的文章
- VPN
- Android软键盘的弹出和隐藏
- 9-5在类中定义装饰器
- 简述单例设计模式的一些理解及代码实现