本篇博客主要实现以下效果:

  • 使用FragmentTabHost实现qq底部Tab切换
  • 使用RadioGroup和RadioButton实现仿qq底部切换
  • 使用RadioGroup和ViewPager 实现可以滑动切换的仿qq底部Tab切换
  • 解决Fragment多次实例化的几种方案
  • Fragemnt的懒加载(网上很多人称之为Fragemnt的最优加载)

效果图

老规矩,废话 不多说,先看效果图

FragmentTabHost实现qq底部Tab实践的效果图

RadioGroup和ViewPager 实现可以滑动切换的仿qq底部Tab效果图

使用FragmentTabHost实现qq底部Tab切换

第一步先看布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><!--用来填充Fragemnt的FrayLayout--><FrameLayoutandroid:id="@+id/main_layout_content"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"></FrameLayout><!--FragmentTabHost--><android.support.v4.app.FragmentTabHostandroid:id="@android:id/tabhost"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#F6F6F6"android:paddingBottom="5dp"android:paddingTop="5dp"><FrameLayoutandroid:id="@android:id/tabcontent"android:layout_width="0dp"android:layout_height="0dp"/></android.support.v4.app.FragmentTabHost></LinearLayout>

其实很简单,没什么好说的 ,就是一个vertical的LinearLayout中放置着一个FrameLayout和FragmentTabHost

接下来我们来看一下代码

public class FirstStyleActivity extends AppCompatActivity {FragmentTabHost mTabHost;private TabWidget mTabWidget;private List<FragmentInfo> mFragmentEntities;private static final String TAG = "xujun";private static final String tag = "tag";public static final String[] mTiltles = new String[]{"首页", "课程", "直播", "个人"};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_first_style);mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);mTabHost.setup(this, getSupportFragmentManager(), R.id.main_layout_content);mTabWidget = mTabHost.getTabWidget();//  去掉分割线mTabWidget.setDividerDrawable(null);mFragmentEntities = MainFragmentFactory.getInstance().getList();initListener();initData();}private void initData() {int size = mFragmentEntities.size();for (int i = 0; i < size; i++) {Log.i(TAG, "size:=" + size);FragmentInfo fragmentInfo = mFragmentEntities.get(i);String title = fragmentInfo.getTitle();TabHost.TabSpec tabSpec = mTabHost.newTabSpec(title).setIndicator(getTabView(i));Bundle bundle = new Bundle();bundle.putString(tag, mTiltles[i]);mTabHost.addTab(tabSpec, fragmentInfo.getClz(), bundle);}updateTab(0);}private void initListener() {mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {@Overridepublic void onTabChanged(String tabId) {int currentTab = mTabHost.getCurrentTab();Log.i(TAG, "onTabChanged:currentTab:=" + currentTab);updateTab(currentTab);}});}private View getTabView(int i) {View view = View.inflate(this, R.layout.tab_layout, null);int currentTab = mTabHost.getCurrentTab();Log.i(TAG, "currentTab:=" + currentTab);setSingleView(view, currentTab, i);return view;}private void setSingleView(View view, int currentTab, int index) {FragmentInfo fragmentInfo = mFragmentEntities.get(index);int[] imagIds = fragmentInfo.getImagIds();int[] colors = fragmentInfo.getColors();TextView tv = (TextView) view.findViewById(R.id.tab_tv);ImageView iv = (ImageView) view.findViewById(R.id.tab_icon);tv.setText(fragmentInfo.getTitle());Resources resources = getResources();if (index == currentTab) {tv.setTextColor(resources.getColor(colors[1]));iv.setImageDrawable(resources.getDrawable(imagIds[1]));} else {tv.setTextColor(getResources().getColor(colors[0]));iv.setImageDrawable(resources.getDrawable(imagIds[0]));}}private void updateTab(int currentTab) {int childCount = mTabWidget.getChildCount();for (int i = 0; i < childCount; i++) {View view = mTabWidget.getChildTabViewAt(i);setSingleView(view, currentTab, i);}}
}

其实说起来也很简单,主要分为以下步骤

  • 第一步,实例化FragmentTabHost并设置相关样式
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);mTabHost.setup(this, getSupportFragmentManager(), R.id.main_layout_content);
mTabWidget = mTabHost.getTabWidget();
//  去掉分割线
mTabWidget.setDividerDrawable(null);
  • 第二步,添加每一个TabHost
 for (int i = 0; i < size; i++) {Log.i(TAG, "size:=" + size);FragmentInfo fragmentInfo = mFragmentEntities.get(i);String title = fragmentInfo.getTitle();TabHost.TabSpec tabSpec = mTabHost.newTabSpec(title).setIndicator(getTabView(i));Bundle bundle = new Bundle();bundle.putString(tag, mTiltles[i]);mTabHost.addTab(tabSpec, fragmentInfo.getClz(), bundle);
}
  • 第三步,通过设置 监听器来实现底部tab颜色和图案样式的转换
 mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {@Overridepublic void onTabChanged(String tabId) {int currentTab = mTabHost.getCurrentTab();Log.i(TAG, "onTabChanged:currentTab:=" + currentTab);updateTab(currentTab);}
});

运行上述代码及可以看到如下效果图


使用RadioGroup和RadioButton实现仿qq底部切换

第一步 ,先看布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutandroid:id="@+id/activity_second_style"xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"
><!--用来填充Fragemnt的FrayLayout--><FrameLayoutandroid:id="@+id/fl"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"></FrameLayout><!--使用RadioGroup来实现tab的切换--><RadioGroupandroid:id="@+id/rg"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/rb_home"style="@style/bottom_tab"android:drawableTop="@drawable/sel_home"android:text="首页"/><RadioButtonandroid:id="@+id/rb_course"style="@style/bottom_tab"android:drawableTop="@drawable/sel_course"android:text="课程"/><RadioButtonandroid:id="@+id/rb_direct_seeding"style="@style/bottom_tab"android:drawableTop="@drawable/sel_direct_seeding"android:text="直播"/><RadioButtonandroid:id="@+id/rb_me"style="@style/bottom_tab"android:drawableTop="@drawable/sel_me"android:text="我的"/></RadioGroup></LinearLayout>

其实每一个tab的选中时利用RadioGroup中RadioButton的互相排斥的特性,即每一次只能选中一个 RadioButton

至于bottom_tab的style,只不过是将相同的arr提取出来,减少布局的代码量和方便统一修改而已,平时我们在写布局代码 的时候也可以这样

<style name="bottom_tab"><item name="android:layout_width" >0dp</item><item name="android:layout_height" >wrap_content</item><item name="android:layout_weight" >1</item><item name="android:text" >0dp</item><item name="android:gravity" >center</item><item name="android:textColor" >@drawable/sel_bottom_tab_text</item><item name="android:padding" >8dp</item><item name="android:button" >@null</item>
</style>

第二步,我们来看一下Activity的 代码

public class ThreeActivity extends AppCompatActivity {FrameLayout mFl;RadioGroup mRg;private FragmentManager mFragmentManager;private int position = 0;public static final String[] mTiltles = new String[]{"首页", "课程", "直播", "个人"};private List<Fragment> mFragments;private Fragment mCurFragment;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_three);mFl = (FrameLayout) findViewById(R.id.fl);mRg = (RadioGroup) findViewById(R.id.rg);mFragments = new ArrayList<>();for (int i = 0; i < mTiltles.length; i++) {ItemFragement itemFragement = ItemFragement.newInstance(mTiltles[i]);mFragments.add(itemFragement);}mCurFragment = mFragments.get(position);replaceFragment(mCurFragment);((RadioButton)mRg.getChildAt(position)).setChecked(true);initListener();}private void initListener() {mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {RadioButton radioButton = (RadioButton) group.findViewById(checkedId);if (false == radioButton.isChecked()) {return;}switch (checkedId) {case R.id.rb_home:position = 0;break;case R.id.rb_course:position = 1;break;case R.id.rb_direct_seeding:position = 2;break;case R.id.rb_me:position = 3;break;default:position = 0;break;}LUtils.i("position==" + position);Fragment to = mFragments.get(position);showFragment(mCurFragment, to);mCurFragment = to;}});}private void showFragment(Fragment from, Fragment to) {FragmentManager supportFragmentManager = getSupportFragmentManager();FragmentTransaction transaction = supportFragmentManager.beginTransaction();if (!to.isAdded()) {    // 先判断是否被add过transaction.hide(from).add(R.id.fl, to).commit(); // 隐藏当前的fragment,add下一个到Activity中} else {transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个}}/*** 这个方法用老替换fragment* xujun* 2016/5/3 17:28.*/private void replaceFragment(Fragment fragmeny) {FragmentManager supportFragmentManager = getSupportFragmentManager();FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();fragmentTransaction.replace(R.id.fl, fragmeny).commit();}}

思路解析

  • 实例化各个控件,这里代码就不贴出来了
  • 初始化 Fragemnt 和选中各个tab
 mFragments = new ArrayList<>();
for (int i = 0; i < mTiltles.length; i++) {ItemFragement itemFragement = ItemFragement.newInstance(mTiltles[i]);mFragments.add(itemFragement);
}
mCurFragment = mFragments.get(position);
replaceFragment(mCurFragment);((RadioButton)mRg.getChildAt(position)).setChecked(true);private void replaceFragment(Fragment fragmeny) {FragmentManager supportFragmentManager = getSupportFragmentManager();FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();fragmentTransaction.replace(R.id.fl, fragmeny).commit();
}
  • 第三步,通过监听RadioGroup的 OnCheckedChangeListener事件,来实现tab和Fragemnt的切换
 mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {RadioButton radioButton = (RadioButton) group.findViewById(checkedId);if (false == radioButton.isChecked()) {return;}switch (checkedId) {case R.id.rb_home:position = 0;break;case R.id.rb_course:position = 1;break;case R.id.rb_direct_seeding:position = 2;break;case R.id.rb_me:position = 3;break;default:position = 0;break;}LUtils.i("position==" + position);Fragment to = mFragments.get(position);showFragment(mCurFragment, to);mCurFragment = to;}});

使用RadioGroup和ViewPager 实现可以滑动切换的仿qq底部Tab切换

第一步,我们 同样先看布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutandroid:id="@+id/activity_second_style"xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"
><!--用来填充Fragemnt的ViewPager--><android.support.v4.view.ViewPagerandroid:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"></android.support.v4.view.ViewPager><!--使用RadioGroup来实现tab的切换--><RadioGroupandroid:id="@+id/rg"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><RadioButtonandroid:id="@+id/rb_home"style="@style/bottom_tab"android:drawableTop="@drawable/sel_home"android:text="首页"/><RadioButtonandroid:id="@+id/rb_course"style="@style/bottom_tab"android:drawableTop="@drawable/sel_course"android:text="课程"/><RadioButtonandroid:id="@+id/rb_direct_seeding"style="@style/bottom_tab"android:drawableTop="@drawable/sel_direct_seeding"android:text="直播"/><RadioButtonandroid:id="@+id/rb_me"style="@style/bottom_tab"android:drawableTop="@drawable/sel_me"android:text="我的"/></RadioGroup></LinearLayout>

第二步,我们一起来看一下Activity代码

public class SecondStyleActivity extends AppCompatActivity {public static final String[] mTiltles = new String[]{"首页", "课程", "直播", "个人"};private List<Fragment> mFragments;ViewPager mViewPager;RadioGroup mRg;private int position = 0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second_style);mViewPager = (ViewPager) findViewById(R.id.viewPager);mRg = (RadioGroup) findViewById(R.id.rg);initListener();initData();}private void initData() {mFragments = new ArrayList<>();for (int i = 0; i < mTiltles.length; i++) {ItemFragement itemFragement = ItemFragement.newInstance(mTiltles[i]);mFragments.add(itemFragement);}BaseFragmentAdapter fragmentAdapter = new BaseFragmentAdapter(getSupportFragmentManager(), mFragments, mTiltles);mViewPager.setAdapter(fragmentAdapter);mViewPager.setCurrentItem(position);((RadioButton) mRg.getChildAt(position)).setChecked(true);}private void initListener() {mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {@Overridepublic void onPageSelected(int position) {RadioButton radioButton = (RadioButton) mRg.getChildAt(position);radioButton.setChecked(true);}});mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {RadioButton rb = (RadioButton) group.findViewById(checkedId);if (!rb.isChecked()) {return;}switch (checkedId) {case R.id.rb_home:position = 0;break;case R.id.rb_course:position = 1;break;case R.id.rb_direct_seeding:position = 2;break;case R.id.rb_me:position = 3;break;default:position = 0;break;}mViewPager.setCurrentItem(position);}});}
}

思路解析如下

  • 实例化ViewPager和RadioGroup
 mViewPager = (ViewPager) findViewById(R.id.viewPager);mRg = (RadioGroup) findViewById(R.id.rg);
  • 第二步,初始化ViewPager的适配器和选中 那个tab
 mFragments = new ArrayList<>();
for (int i = 0; i < mTiltles.length; i++) {ItemFragement itemFragement = ItemFragement.newInstance(mTiltles[i]);mFragments.add(itemFragement);
}
BaseFragmentAdapter fragmentAdapter = new BaseFragmentAdapter(getSupportFragmentManager(), mFragments, mTiltles);
mViewPager.setAdapter(fragmentAdapter);
mViewPager.setCurrentItem(position);
((RadioButton) mRg.getChildAt(position)).setChecked(true);
  • 第三步,监听ViewPager 的滑动事件和RadioGroup的OnCheckedChangeListener事件,分别切换到相应的 Fragemnt 和同步ViewPager中 position 与RadioGroup之间的联系
 mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {@Overridepublic void onPageSelected(int position) {RadioButton radioButton = (RadioButton) mRg.getChildAt(position);radioButton.setChecked(true);}
});mRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {@Overridepublic void onCheckedChanged(RadioGroup group, int checkedId) {RadioButton rb = (RadioButton) group.findViewById(checkedId);if (!rb.isChecked()) {return;}switch (checkedId) {case R.id.rb_home:position = 0;break;case R.id.rb_course:position = 1;break;case R.id.rb_direct_seeding:position = 2;break;case R.id.rb_me:position = 3;break;default:position = 0;break;}mViewPager.setCurrentItem(position);}
});

注意事项

我们可以通过以下方法设置ViewPager左右页面 能缓存的fragment 数量

//  设置左右页面 能缓存的fragment 数量
mViewPager.setOffscreenPageLimit(fragmentAdapter.getCount() - 1);

到此仿qq底部tab切换的集中方法已经讲解完毕,之所以讲解了 三种方法,是想让大家了解多种实现方式,因为每一个人的习惯都不一样,有些人习惯使用与第一种方式,有人习惯使用第二种方式 。。。。。。了解多种 实现方式以后,我们要读懂别人的代码也容易得多了,其实我们还可以使用自定义控件来实现,方法也比较简单,这里就不讲解了,有兴趣的话,可以自行搜索

下面我将为大家讲解Fragment的 一些优化


解决Fragment多次实例化的几种方案

目前本人了解到的解决方案 ,无非是利用一下两种思想

第一种解决方法

在onCreateView中避免多次实例化View,可通过判断View是否为空,来实现相应的 逻辑操作,核心代码如下

   public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @NullableBundle savedInstanceState) {LUtils.i(getClass().getSimpleName()+">>>>>>>>>>>  onCreateView");if(mView==null){mContext = getContext();mView=View.inflate(mContext,getLayoutId(),null);initView(mView);LUtils.i(getClass().getSimpleName()+">>>>>>>>>>>  initView");}else{// 缓存的rootView需要判断是否已经被加过parent,如果有parent需要从parent删除,// 要不然会发生这个rootview已经有parent的错误。ViewGroup parent =(ViewGroup) mView.getParent();if(parent!=null){parent.removeView(mView);}LUtils.i(getClass().getSimpleName()+">>>>>>>>>>>  removeView");}return mView;}

第二种解决方案

在项目中需要进行Fragment的切换,用hide()和show()方法结合起来来替代replace()方法来实现Fragment的切换:

private void showFragment(Fragment from, Fragment to) {FragmentManager supportFragmentManager = getSupportFragmentManager();FragmentTransaction transaction = supportFragmentManager.beginTransaction();if (!to.isAdded()) {    // 先判断是否被add过transaction.hide(from).add(R.id.fl, to).commit(); // 隐藏当前的fragment,add下一个到Activity中} else {transaction.hide(from).show(to).commit(); // 隐藏当前的fragment,显示下一个}}/*** 这个方法用老替换fragment* xujun* 2016/5/3 17:28.*/private void replaceFragment(Fragment fragmeny) {FragmentManager supportFragmentManager = getSupportFragmentManager();FragmentTransaction fragmentTransaction = supportFragmentManager.beginTransaction();fragmentTransaction.replace(R.id.fl, fragmeny).commit();
}

关于 避免Fragment的多次实例化的分析与优化到此为止,下面我们一起来看一下 则那样实现Fragemnt 的 懒加载


Fragemnt的懒加载(网上很多人称之为Fragemnt的最优加载)

关于懒加载的这部分,代码是参考这篇 博客的 ViewPager+Fragment LazyLoad最优解

我们知道 ViewPager通常 会有预加载机制,默认情况下会先加载左右一页的数据,有时候我们想等待页面可见的时候在去加载网络 数据 ,解决方案如下

下面 先贴出代码

public abstract class BasePageFragment extends Fragment {protected View mView;/*** 表示View是否被初始化*/protected boolean isViewInitiated;/*** 表示对用户是否可见*/protected boolean isVisibleToUser;/*** 表示数据是否初始化*/protected boolean isDataInitiated;private Context mContext;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);LUtils.i(getClass().getSimpleName() + ">>>>>>>>>>>  onCreate");}@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @NullableBundle savedInstanceState) {LUtils.i(getClass().getSimpleName() + ">>>>>>>>>>>  onCreateView");if (mView == null) {mContext = getContext();mView = View.inflate(mContext, getLayoutId(), null);initView(mView);LUtils.i(getClass().getSimpleName() + ">>>>>>>>>>>  initView");} else {// 缓存的rootView需要判断是否已经被加过parent,如果有parent需要从parent删除,// 要不然会发生这个rootview已经有parent的错误。ViewGroup parent = (ViewGroup) mView.getParent();if (parent != null) {parent.removeView(mView);}LUtils.i(getClass().getSimpleName() + ">>>>>>>>>>>  removeView");}return mView;}protected abstract void initView(View view);protected abstract int getLayoutId();@Overridepublic void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);LUtils.i(getClass().getSimpleName() + ">>>>>>>>>>>  onActivityCreated");isViewInitiated = true;initData();prepareFetchData();}@Overridepublic void setUserVisibleHint(boolean isVisibleToUser) {super.setUserVisibleHint(isVisibleToUser);this.isVisibleToUser = isVisibleToUser;prepareFetchData();}public abstract void fetchData();public boolean prepareFetchData() {return prepareFetchData(false);}/**** * @param forceUpdate 表示是否在界面可见的时候是否强制刷新数据* @return*/public boolean prepareFetchData(boolean forceUpdate) {if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {//  界面可见的时候再去加载数据fetchData();isDataInitiated = true;return true;}return false;}@Overridepublic void onDestroyView() {super.onDestroyView();LUtils.i(getClass().getSimpleName() + ">>>>>>>>>>>  onDestroyView");}protected void initData() {}}

思路解析:
其实核心思想主要在setUserVisibleHint(boolean isVisibleToUser)方法中,这个方法在Fragemnt界面切换到可见或者 不可见的时候调用,isVisibleToUser表示当前界面可见 或者不可见

setUserVisibleHint(boolean isVisibleToUser)

Set a hint to the system about whether this fragment’s UI is currently visible to the user.

于是我们在prepareFetchData进行判断只有当界面可见的情况下才会尝试判断是否调用fetchData() 方法,于是我们就可以实现等到界面可见的 时候才加载网络数据,将我们加载网络数据的 逻辑的实现放在fetchData()
里面即可


/***** @param forceUpdate 表示是否在界面可见的时候是否强制刷新数据* @return*/
public boolean prepareFetchData(boolean forceUpdate) {if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {//  界面可见的时候再去加载数据fetchData();isDataInitiated = true;return true;}return false;
}

题外话

转眼间校招季已经过去了一个多月了,虽然没能进入到bat,网易之类的公司,不过也找到了相对比较喜欢的公司,接下来的日子就专心做毕业设计了,计划在十一月的时候出省旅游一次,明年春节回来的时候再去公司实习,悄悄地告诉你一件事哦,本人 还没有 出过广东省 ,哈哈,是不是很low呢?

转载请注明博客地址:http://blog.csdn.net/gdutxiaoxu/article/details/52826810

源码下载地址:https://github.com/gdutxiaoxu/QQBottomTab.git

仿qq底部Tab导航相关推荐

  1. android仿咸鱼底部导航栏,Flutter沉浸式状态栏/AppBar导航栏/仿咸鱼底部凸起导航栏效果...

    如下图:状态栏是指android手机顶部显示手机状态信息的位置. android 自4.4开始新加入透明状态栏功能,状态栏可以自定义颜色背景,使titlebar能够和状态栏融为一体,增加沉浸感. 如上 ...

  2. Laysns内核仿qq技术网址导航网整站源码

    介绍: Laysns内核仿qq技术网址导航网整站源码基于laysns开发,打包的也只是模板文件,使用前需要自行下载安装laysns主程序,然后再将模板文件夹上传到template文件夹下替换即可使用. ...

  3. android的动态tab,Android自定义view仿QQ的Tab按钮动画效果(示例代码)

    话不多说 先上效果图 实现其实很简单,先用两张图 一张是背景的图,一张是笑脸的图片,笑脸的图片是白色,可能看不出来.实现思路:主要是再触摸view的时候同时移动这两个图片,但是移动的距离不一样,造成的 ...

  4. 仿美丽说底部TAB布局实现

    今天下载美丽说应用,感觉它底部TAB布局挺特别的,于是模仿实现了一下.素材资源部分取自美丽说APK,仅研究.学习之用! 效果图: maintabs.xml <?xml version=" ...

  5. android仿京东底部tab,GitHub - wenchaosong/BottomBar: 仿京东底部栏重复选择刷新动画,还有普通的样式和 MaterialDesign 样式...

    轻量级的底部导航栏 在原项目PagerBottomTabStrip 基础上 增加了 getItem 方法,能设置对应 position 的 tab 属性 实现效果图 自定义扩展例子 仿京东重复刷新动画 ...

  6. android高仿微信底部渐变导航栏

    最近有很多人问微信底部的变色卡片导航是怎么做的,我在网上看了好几个例子,都是效果接近,都存有一些差异,自己琢磨也做了一个,几乎99%的还原,效果还不错吧 仔细观察微信图片,发现他有两部分内容,外面的边 ...

  7. Android仿QQ微信开场导航以及登陆界面

    相信大家对于微信等社交应用的UI界面已经都很熟悉了,该UI最值得借鉴的莫过于第一次使用的时候一些列产品介绍的图片,可以左右滑动浏览,最后进入应用,这一效果适用于多种项目中,相信今后开发应用一定会用得到 ...

  8. 底部带导航的android app,【续】Android App之底部tab导航常用实现方案总结

    动态效果图就不贴出来了,来张静态图吧: 实现的效果还算完美吧. 看看我们的FragmentTabHost方式修改的代码部分, 1,FragmentTabHost中的修改 因为在原来的tab数量上增加了 ...

  9. Android自定义view仿QQ的Tab按钮动效

    话不多说 先上效果图 实现其实很简单,先用两张图 一张是背景的图,一张是笑脸的图片,笑脸的图片是白色,可能看不出来.实现思路:主要是再触摸view的时候同时移动这两个图片,但是移动的距离不一样,造成的 ...

最新文章

  1. 【直播预告】7月18日3D游戏引擎免费公开课答疑,參与送C币!
  2. RoIPooling
  3. HEX、DEC、OCT和BIN的解释
  4. IBASE Read buffer invalidate when locked
  5. 编程猫获新一轮 3 亿融资:做好少儿编程教育基础设施!
  6. 1004. 成绩排名 (20)-PAT乙级真题
  7. Codeforces #123D: 后缀数组+单调栈
  8. 记一个函数定义中,形参是空列表时要注意的问题
  9. mtk 充电出错问题
  10. VLD在VS2019中的使用以及应用程序无法正常启动(0xc0150002)
  11. 人工神经网络心得体会_人工智能学习心得
  12. Windows10开机进不了BIOS的解决办法
  13. Python3 unicode编码 \xe4\xbd\xa0 和 \\u884c\\u4e1a\\u5206 转中文
  14. LOL IXTAL CUP 第一日 冠军杯赛 407的夺冠之路
  15. win10激活错误,软件授权服务报告无法激活计算机怎么办?
  16. 2018ccpc网络赛1010 J - YJJ's Salesman
  17. three js 报错, 贴图黑乎乎
  18. Ant工具 ant的安装与配置 ant作用
  19. js 复制微信号码 并唤醒微信
  20. android studio查看源码

热门文章

  1. ping和taskkill和cmd和avac命令的详解
  2. Ubuntu 微信 linux版最新可用——UOS魔改版
  3. 元宇宙区块链游戏开发 元宇宙手机游戏开发
  4. Linux命令使用技巧集锦(转)
  5. FCKEditor 使用
  6. Yuan先生博客-Django基础
  7. lqc_远程日志管理
  8. 特斯拉的巫术(5)——“彩蛋”和未来
  9. 历届“世界足球先生”档案
  10. 一个显示器分屏显示两个画面_测了两个爆款游戏显示器,结果我发现他们都有坑。...