您的位置:首页 > 移动开发 > Android开发

安卓(Android)ViewPager+TabLayout实现图片轮播效果

2016-11-09 22:01 1281 查看
起因:
最近在做一个新闻APP,看到现在的新闻客户端顶端都有个热点新闻轮播。

思路:viewpager可以用来显示图片,并且可以提供滑动,15年(不知记错没)新出的TabLayout可以绑定ViewPager的Adapter来实现ViewPager与Tablayout的联动效果,比如qq的下面菜单栏,微信的菜单栏,都可以这样实现。而且我们可以通过两种方式来实现TabLayout的图片标题:

1.SpannableString 来实现图文混排,混排方法(传送门)。

2.使用自定义布局,布局中添加你想要显示的内容,然后调用tablayout中tab的setCustomView(View view)或者setCustomView(int R_id)方法来设置显示。

下面正题:

这里我使用第二种方法来实现,至于为什么用第二种(好吧其实是我使用第一种的时候遇到了一个不显示的bug,当然第二种也遇到了,后面会提及,后来就没改回第一种了,原理一样)源码在最后面有下载。

一。首先我们来准备图片:

没有选中的位置的提示图片:






选中了之后的位置的提示图片:






图片都可以使用ps制作,很简单。

二。然后我们来写轮播界面的布局(老规矩,先代码后解释):

<?xml version="1.0" encoding="utf-8"?>
<!--轮播viewpager布局-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="200dp">
<android.support.design.widget.TabLayout
app:tabIndicatorColor="@color/colorTransparent"
android:id="@+id/luobo_tablayout"
android:layout_width="100dp"
android:layout_height="10dp"
app:tabMinWidth="10dp"
app:tabMaxWidth="30dp"
app:tabGravity="center"
app:tabMode="fixed"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:translationZ="2dp"
android:layout_marginBottom="5dp"/>
<android.support.v4.view.ViewPager
android:id="@+id/luobo_viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:translationZ="0dp"
>
</android.support.v4.view.ViewPager>
</RelativeLayout>
这里解释几个点:

(1)translationZ属性:为了让Tablayout显示在Viewpager的上方,不设置z值大于ViewPager的话,就显示不出来了。

(2)tabMode属性:为了不让tablayout滑动(在内容超出控件宽度的时候里面内容滑动从而造成显示上的逻辑错误)。

(3)tabMinWidth属性:

           tabMaxWidth属性:设置tab的最小值和最大值,来控制每个tab的宽度(先立个Flag)。

三。现在我们来为ViewPager的内容写布局文件,这里我们的内容有新闻图片(覆盖全页面),新闻标题(位于左下角),

布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/imageLayout_Image"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@mipmap/winter_news_place_hloder_pic"
android:src="@color/colorTransparent"/>
<TextView
android:id="@+id/imageLayout_Title"
android:textColor="@color/colorWrite"
android:layout_width="200dp"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="1"
android:layout_height="20dp"
android:layout_alignParentBottom="true"
android:textAlignment="viewStart"
android:layout_marginLeft="15dp"/>
</RelativeLayout>
这里设置了图片为默认的占位图片。可根据喜好设置。

四。既然我们要自定义tablayout的布局,那么我们现在就来写这个布局文件

布局很简单,就一个ImageView,为了拉伸图片到全部页面,我们这里设置他的background属性而不是src属性:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/Imageview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/luobo_unseleted"/>
</LinearLayout>


五。布局我们已经弄好了,接下来就是逻辑代码了,我这里使用继承自Fragment是为了方便插入任何其他布局里,我这里插入的是首页。

这里插播一下布局中插入碎片(Fragment)的方法,老规矩:

<fragment
android:layout_width="match_parent"
android:layout_height="200dp"
android:id="@+id/luoboViewPaget"
android:name="lunbo.viewPagerLuobo"/>


注意上面的name属性是必要的,用来指向Fragment的定义,其他的属性和一般的一样设置就可以了。

现在可以写轮播的Fragment的代码了(很简单的逻辑,我就不解释了,重要的地方有注释):

package lunbo;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.icu.text.LocaleDisplayNames;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.WindowDecorActionBar;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;

import java.util.ArrayList;

import NewsInfo.JUHE_NewsInfo;
import Static.StaticValue;
import Util.FileOP.FileStore;
import Util.NetWork;
import winter.zxb.smilesb101.winternews.R;

/**
* 项目名称:WinterNews
* 类描述:图片轮播类
* 创建人:SmileSB101
* 创建时间:2016/11/9 0009 07:36
* 修改人:Administrator
* 修改时间:2016/11/9 0009 07:36
* 修改备注:
*/

public class viewPagerLuobo extends Fragment{
private TabLayout mTablayout;
private ViewPager mViewPager;
private int selectNum;
private int pageNum;
private View view;
private ArrayList<JUHE_NewsInfo> juhe_newsInfos = new ArrayList<>();
private viewPagerAdapter mViewPageAdapter;
private static final int GetNewsDown = 0;
private static final int GetNewsError = -1;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
switch(msg.what)
{
case GetNewsDown:
Log.i("设置轮播图片显示","handleMessage: ");
showTopNews();
break;
case GetNewsError:
break;
}
}
};
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,@Nullable ViewGroup container,@Nullable Bundle savedInstanceState){
view = inflater.inflate(R.layout.lunbo_viewpager,container,false);
selectNum = 0;
pageNum = 3;
getTopNews();
return view;
}
private void getTopNews()
{
RequestQueue mrequeue = NetWork.getmRequestqueue(StaticValue.MainActivity);
String utl = "http://v.juhe.cn/toutiao/index?top&key=2d89bbcc668f4ef02cade2b00d7d9265";
StringRequest stringRequest = new StringRequest(utl,new Response.Listener<String>(){
@Override
public void onResponse(String s){
Log.i("获取到头条","onResponse: ");
juhe_newsInfos = FileStore.getJUHENews_List(s);
handler.sendEmptyMessage(GetNewsDown);
}
},new Response.ErrorListener(){
@Override
public void onErrorResponse(VolleyError volleyError){
handler.sendEmptyMessage(GetNewsError);
}
});
mrequeue.add(stringRequest);
}
private void showTopNews()
{
mViewPager = (ViewPager)view.findViewById(R.id.luobo_viewpager);
mViewPageAdapter = new viewPagerAdapter(getFragmentManager());
mViewPager.setAdapter(mViewPageAdapter);
setmTablayout();
}
private void setmTablayout()
{
mTablayout = (TabLayout)view.findViewById(R.id.luobo_tablayout);
mTablayout.setupWithViewPager(mViewPager);
//添加tab,这里添加一开始显示的图片,重要代码
for(int i = 0;i<mViewPageAdapter.getCount();i++) {
TabLayout.Tab tab = mTablayout.getTabAt(i);
Log.i("添加图片","setmTablayout: 1");
View v = LayoutInflater.from(StaticValue.MainActivity).inflate(R.layout.luobo_tablayout,null);
ImageView tabImage = (ImageView)v.findViewById(R.id.Imageview);
tabImage.setBackgroundResource(R.mipmap.luobo_unseleted);
tab.setCustomView(v);
}
//添加图片变换,在这里进行选项变化时图片的设置,重要代码
mTablayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener(){
@Override
public void onTabSelected(TabLayout.Tab tab){
Log.i("图片添加","onTabSelected: ");
View v = tab.getCustomView();
ImageView tabImage = (ImageView)v.findViewById(R.id.Imageview);
tabImage.setBackgroundResource(R.mipmap.luobo_seleted);
//tab.setCustomView(v);
}

@Override
public void onTabUnselected(TabLayout.Tab tab){
View v = tab.getCustomView();
ImageView tabImage = (ImageView)v.findViewById(R.id.Imageview);
tabImage.setBackgroundResource(R.mipmap.luobo_unseleted);
//tab.setCustomView(v);
}
@Override
public void onTabReselected(TabLayout.Tab tab){
}
});
//后台开启计时器线程,每3秒设置一次选项变化,实现轮播的逻辑,重要代码。
handler.postDelayed(new Runnable(){
@Override
public void run(){
selectNum = selectNum%pageNum;
Log.i("选项卡数量","run: "+selectNum);
mViewPager.setCurrentItem(selectNum);
selectNum++;
handler.postDelayed(this,3000);
}
},0);
}

class viewPagerAdapter extends FragmentPagerAdapter{
public viewPagerAdapter(FragmentManager fm){
super(fm);
}

@Override
public int getCount(){
return pageNum;
}

@Override
public Fragment getItem(int position){
return ImageFragment.newInstence(juhe_newsInfos.get(position));
}

@Override
public CharSequence getPageTitle(int position){
return "";
}
}
}


获取新闻数据接口是来自聚合网,

下面贴出轮播Viewpager中的内容Fragment:

package lunbo;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.SimpleTarget;

import NewsInfo.JUHE_NewsInfo;
import winter.zxb.smilesb101.winternews.R;

/**
* 项目名称:WinterNews
* 类描述:轮播ViewPager中显示的内容的Fragment
* 创建人:SmileSB101
* 创建时间:2016/11/9 0009 13:24
* 修改人:Administrator
* 修改时间:2016/11/9 0009 13:24
* 修改备注:
*/

public class ImageFragment extends Fragment{
private ImageView mImageView;
private JUHE_NewsInfo juhe_newsInfo;
private TextView title;
Fragment context;
public void setJuhe_newsInfo(JUHE_NewsInfo juhe_newsInfo)
{
this.juhe_newsInfo = juhe_newsInfo;
}
public ImageFragment(){
}
public static ImageFragment newInstence(JUHE_NewsInfo juhe_newsInfo)
{
ImageFragment imageFragment = new ImageFragment();
imageFragment.setJuhe_newsInfo(juhe_newsInfo);
return imageFragment;
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater,@Nullable ViewGroup container,@Nullable Bundle savedInstanceState){
View rootView = null;
context = this;
rootView = inflater.inflate(R.layout.image_layout,container,false);
title = (TextView)rootView.findViewById(R.id.imageLayout_Title);
mImageView = (ImageView)rootView.findViewById(R.id.imageLayout_Image);
if(juhe_newsInfo!=null) {
Glide.with(context)
.load(juhe_newsInfo.getThumbnail_pic_s())
.placeholder(R.mipmap.winter_news_place_hloder_pic)
.error(R.mipmap.winter_news_error_pic)
.into(new SimpleTarget<GlideDrawable>(){
@Override
public void onResourceReady(GlideDrawable resource,GlideAnimation<? super GlideDrawable> glideAnimation){
//在这里设置图片背景(当GLide获取完成图片之后执行)
mImageView.setBackground(resource);
}
});
title.setText(juhe_newsInfo.getTitle());
}
else
{
Glide.with(context)
.load(R.mipmap.winter_news_place_hloder_pic)
.error(R.mipmap.winter_news_error_pic)
.into(new SimpleTarget<GlideDrawable>(){
@Override
public void onResourceReady(GlideDrawable resource,GlideAnimation<? super GlideDrawable> glideAnimation){
//在这里设置图片背景(当GLide获取完成图片之后执行)
mImageView.setBackground(resource);
}
});
}
return rootView;
}
}


图片加载我使用的是Glide库,详细的使用方法(点击开启传送门)。

下面说一下我遇到的bug,在包含tablayout的布局中,没有设置tab的最大宽度值,导致数量多了之后看不到背景图片(这么简单的问题,蜗居然改本来就是能用的代码改了大半天!!后来突然想到改少tab数量会不会有效才发现问题所在,在这里做个记录,以防再次入坑。)

下面是截图(可以看到效果已经实现了,但是tab间距太远):






于是我们调整一下tablayout的padding值以及tabMaxWidth的值:

改变之后的tablayout如下:

<android.support.design.widget.TabLayout
app:tabIndicatorColor="@color/colorTransparent"
android:id="@+id/luobo_tablayout"
android:layout_width="100dp"
android:layout_height="10dp"
app:tabMinWidth="10dp"
app:tabMaxWidth="10dp"
app:tabPadding="0dp"
app:tabPaddingStart="0dp"
app:tabPaddingEnd="0dp"
app:tabGravity="center"
app:tabMode="fixed"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:translationZ="2dp"
android:layout_marginBottom="5dp"/>
这里控件的宽度可以自己根据实际需求更改

注意必须三个padding和MaxWidth同时设置才能有如下效果,缺一不可!!

注意必须三个padding和MaxWidth同时设置才能有如下效果,缺一不可!!

注意必须三个padding和MaxWidth同时设置才能有如下效果,缺一不可!!

最后截图,原谅我没有截成gif。实在不会用咔咔截屏。。。。

如有问题,欢迎指正。



现在来看看效果:






源码传送门
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息