Android仿制淘宝滚动图文条

96
一个有故事的程序员
2018.08.17 19:05* 字数 469

开篇废话

产品让我将“我的”页面改版,上面加了一个广告条,非常类似淘宝“我的”页面的广告条,然后就自己写了一个,虽然网上类似的教程有很多了,但是我用的这个实现方法我没有见过,如果大家有更好的办法请留言给我,谢谢。这里只提供一种思路,有兴趣的小伙伴可以自行封装一下,记得告知我。滚动图文条之GitHub地址,帮我点个Star。

滚动图文条

大概效果就是下图这样。

滚动图文条

思路

  1. 写一个不可用手滑动的RecyclerView
  2. 使用Handler定时RecyclerView自动滑动到下一个Item
  3. 使用smoothScrollToPosition使其平滑地滑动

开始工作

做一些基本工作

写一个AdModel类。

public class AdModel {

    public String title;
    public String content;

    public AdModel(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

写一些item_ad布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:minHeight="50dp"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    android:background="@null"
    tools:background="@color/black">

    <LinearLayout
        android:id="@+id/ll_ad"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_title"
            tools:text="会员身份0元抢"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:lines="1"
            android:textColor="@color/white"
            android:textSize="12sp" />

        <TextView
            android:id="@+id/tv_content"
            tools:text="送你体验会员"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:lines="1"
            android:textColor="@color/white"
            android:textSize="10sp" />

    </LinearLayout>

    <View
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <ImageView
        android:id="@+id/iv_icon"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_marginRight="15dp"
        android:src="@drawable/icon"/>

</LinearLayout>

写AdAdapter类。

public class AdAdapter extends RecyclerView.Adapter<AdAdapter.ViewHolder> {

    private Context mContext;
    private OnItemClickListener onItemClickListener;
    private LayoutInflater mInflater;
    private List<AdModel> mDataList;

    public AdAdapter(Context context, List<AdModel> datas) {
        this.mContext = context;
        mDataList = datas;
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
        View itemView = mInflater.inflate(R.layout.item_ad, null);
        return new ViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int p) {
        if (mDataList == null || mDataList.size() ==0){
            return;
        }
        if (holder != null) {
            final int position = p % mDataList.size();

            holder.mTvTitle.setText(mDataList.get(position).title);
            holder.mTvContent.setText(mDataList.get(position).content);
            holder.mIvIcon.setImageResource(R.drawable.icon);

            holder.viewRoot.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onItemClickListener != null) {
                        onItemClickListener.onItemClick(v, position);
                    }
                }
            });
        }
    }

    public void setOnItemClickListener(OnItemClickListener clickListener) {
        this.onItemClickListener = clickListener;
    }

    @Override
    public int getItemCount() {
        return mDataList == null ? 0 : Integer.MAX_VALUE;
    }

    public class ViewHolder extends RecyclerView.ViewHolder {

        public View viewRoot;
        public TextView mTvTitle;
        public TextView mTvContent;
        public ImageView mIvIcon;

        public ViewHolder(View itemView) {
            super(itemView);
            viewRoot = itemView.findViewById(R.id.layout);
            mTvTitle = itemView.findViewById(R.id.tv_title);
            mTvContent = itemView.findViewById(R.id.tv_content);
            mIvIcon = itemView.findViewById(R.id.iv_icon);
        }
    }

    /**
     * RecyclerView的item点击监听接口
     */
    public interface OnItemClickListener {
        void onItemClick(View v, int position);
    }

}

页面activity_main布局。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.cc.scrolladbar.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rv_ad"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_marginEnd="20dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="60dp"
        android:nestedScrollingEnabled="false"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>
重点代码

在MainActivity中写如下代码。

package com.cc.scrolladbar;

import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by guoshichao on 2018/8/16
 * QQ:1169380200
 */

public class MainActivity extends AppCompatActivity {

    private static final int SCROLL_AD = 0;//会员轮播广告
    public static final int DEFAULT_SCROLL_INTERVAL = 3000;//会员轮播广告间隔时间
    public static final float DEFAULT_SCROLL_ANIMATION_TIME = 500;//会员轮播广告动画时长

    private RecyclerView mRvAd;
    private AdAdapter mAdapter;
    private List<AdModel> mAdList;
    private AdHandler mHandler;
    private int nowScrollPosition = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initList();
    }

    @Override
    public void onStart() {
        super.onStart();
        scrollVipAdOnce(nowScrollPosition);//防止滑动一半切到别的页面使滑动完成
        if (mHandler != null) {
            sendScrollMessage(DEFAULT_SCROLL_INTERVAL);
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (mHandler != null) {
            mHandler.removeMessages(SCROLL_AD);
        }
    }

    private void initList() {
        mAdList = new ArrayList<>();
        mAdList.add(new AdModel("第一条广告标题", "我是第一条广告的内容哦~"));
        mAdList.add(new AdModel("第二条广告标题", "我是第二条广告的内容哦~"));
        mAdList.add(new AdModel("第三条广告标题", "我是第三条广告的内容哦~"));

        LinearLayoutManager manager = new LinearLayoutManager(this) {
            @Override
            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
                LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
                    // 为了平滑滑动返回:滑过1px时经历的时间(ms)。
                    @Override
                    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                        return (float) (DEFAULT_SCROLL_ANIMATION_TIME / displayMetrics.densityDpi);
                    }
                };
                smoothScroller.setTargetPosition(position);
                startSmoothScroll(smoothScroller);
            }
        };

        mRvAd = (RecyclerView) findViewById(R.id.rv_ad);
        mRvAd.setLayoutManager(manager);
        mAdapter = new AdAdapter(this, mAdList);
        mRvAd.setAdapter(mAdapter);
        mAdapter.setOnItemClickListener(new AdAdapter.OnItemClickListener() {
            @Override
            public void onItemClick(View v, int position) {
                //点击跳转到指定广告页
            }
        });
        mHandler = new AdHandler();
        sendScrollMessage(DEFAULT_SCROLL_INTERVAL);
    }

    private void scrollVipAdOnce(int position) {
        if (mAdList != null && mAdList.size() > 1) {
            //平滑滑动到指定位置
            mRvAd.smoothScrollToPosition(position);
        }
    }

    private void sendScrollMessage(long delayMillis) {
        mHandler.removeMessages(SCROLL_AD);
        mHandler.sendEmptyMessageDelayed(SCROLL_AD, delayMillis);
    }

    private class AdHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case SCROLL_AD:
                    nowScrollPosition++;
                    scrollVipAdOnce(nowScrollPosition);
                    sendScrollMessage(DEFAULT_SCROLL_INTERVAL);
                    break;
                default:
                    break;
            }
        }
    }

}
重点分析

其中有一段代码比较重要。

LinearLayoutManager manager = new LinearLayoutManager(this) {
            @Override
            public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
                LinearSmoothScroller smoothScroller = new LinearSmoothScroller(recyclerView.getContext()) {
                    // 为了平滑滑动返回:滑过1px时经历的时间(ms)。
                    @Override
                    protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                        //这里必须返回一个正数,否则会出错
                        float duration = DEFAULT_SCROLL_ANIMATION_TIME / displayMetrics.densityDpi;
                        return duration > 0 ? duration : 1;
                    }
                };
                smoothScroller.setTargetPosition(position);
                startSmoothScroll(smoothScroller);
            }
        };

这里是为了平滑滑动。因为距离比较短小或者别的原因,mRvAd.smoothScrollToPosition(position)无法使其平滑地滑动。故加以上代码。

我的完成图如下。

滚动图文条

写在后面

这个Demo比较简单,没什么技术难点,如果还是有些不懂的,可以留言,我在文中可以做更多的解释。如果有大佬有更好的解决方案,望指教。
后来在我的实际使用中,发现在触发滚动的时候,手指也去触摸滑动,就会滑动。暂时没有找到解决办法,不过后来我发现淘宝也有这个BUG,哈哈。

更多内容戳这里(整理好的各种文集)

Android精华教程
Gupao