Android之高仿京东APP首页“京东快报”自动向上滚动的广告条

转载请标明出处:
http://blog.csdn.net/hai_qing_xu_kong/article/details/51013562
本文出自:【顾林海的博客】

##前言

上次在京东APP上买东西时,发现首页中间有块叫“京东快报”的栏目,其中广告条能自动向上滚动,效果还不错,看到这个效果,第一个念头就是我能不能实现,于是就诞生了这篇文章。

我们看看实现后的效果:

向上滚动的广告条

##实现原理

起初看到这个效果时,第一个想法就是向上移动动画+定时器,但当我准备写时发现,滚动时上下文字都是逐渐出来的(有点像滑动的),如果用动画的话不行,如果用滑动的话,那就是ListView了,但在看效果图,界面上明明只有一个ITEM,于是,我进行了以下改动:

  • 设置ListView的高度与Item高度一致,这样界面中就只显示一个Item。
  • 自动滚动,可以使用ListView的smoothScrollBy(int distance, int duration)方法,第一个参数是滚动的距离,第二个是滚动时间。
  • 自动滚动可以通过定时器,使用Handler类自带的postDelyed。
  • ListView的无限向上滚动,可以通过继承BaseAdapter类中重写的getCount()方法时返回Integer.MAX_VALUE使其接近无限大。
  • 触摸事件的拦截,可以通过onInterceptTouchEvent来实现,返回false。

注意:通过smoothScrollBy方法滚动时,由于布局中的高度时dp,但这里面的第一个参数是sp,因此需要将dp转sp,转换完毕后的是float,但方法中是需要int,这样的话有可能导致小数位的丢失,自动滚动产生偏差,可以通过setSelection(int position)方法纠正位置,在smoothScrollBy方法调用后执行setSelection方法。

##控件的使用

根据业务场景不同,如果是主页的广告条,可以创建MainScrollUpAdvertisementView类继承自BaseAutoScrollUpTextView,BaseAutoScrollUpTextView后尖括号中是广告的数据类型,这里假设是AdvertisementObject。

1、创建我们的广告类

package com.example.autoscrollup.entity;

import java.io.Serializable;

/**
 * 获取的首页广告条数据
 * 
 * @author 顾林海
 * 
 */
public class AdvertisementObject implements Serializable {
	public String title;
	public String info;
}

2、创建MainScrollUpAdvertisementView类继承自BaseAutoScrollUpTextView:

package com.example.autoscrollup.view;

import android.content.Context;
import android.util.AttributeSet;

import com.example.autoscrollup.entity.AdvertisementObject;
import com.example.autoscrollup.view.base.BaseAutoScrollUpTextView;

/**
 * <pre>
 * 制作主页的向上广告滚动条
 * AdvertisementObject是主页的数据源,假如通过GSON或FastJson获取的实体类
 * 
 * <pre>
 * @author 顾林海
 * 
 */
public class MainScrollUpAdvertisementView extends
		BaseAutoScrollUpTextView<AdvertisementObject> {

	public MainScrollUpAdvertisementView(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
	}

	public MainScrollUpAdvertisementView(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MainScrollUpAdvertisementView(Context context) {
		super(context);
	}

	@Override
	public String getTextTitle(AdvertisementObject data) {
		return data.title;
	}

	@Override
	public String getTextInfo(AdvertisementObject data) {
		return data.info;
	}

	/**
	 * 这里面的高度应该和你的xml里设置的高度一致
	 */
	@Override
	protected int getAdertisementHeight() {
		return 40;
	}

}

通过getTextTitle、getTextInfo、getAdertisementHeight方法获取标题、内容、以及整个广告条的高度。
这里面的getAdertisementHeight方法返回的高度必须与你将要创建的xml文件中MainScrollUpAdvertisementView 控件高度一致,比如上面是40,那我们创建的xml是这样的:

<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" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp" >

        <ImageView
            android:id="@+id/iv_icon"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:src="@drawable/icon" />

        <com.example.autoscrollup.view.MainScrollUpAdvertisementView
            android:id="@+id/main_advertisement_view"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@id/iv_icon" >
        </com.example.autoscrollup.view.MainScrollUpAdvertisementView>
    </RelativeLayout>

</RelativeLayout>

这里android:layout_height="40dp"设置为40,与我们定义的MainScrollUpAdvertisementView 类中的getAdertisementHeight方法返回的高度一致。

3、在我们的Activity中使用定义的MainScrollUpAdvertisementView 控件:

package com.example.autoscrollup;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

import com.example.autoscrollup.entity.AdvertisementObject;
import com.example.autoscrollup.view.MainScrollUpAdvertisementView;
import com.example.autoscrollup.view.base.BaseAutoScrollUpTextView.OnItemClickListener;

public class MainActivity extends Activity {

	private MainScrollUpAdvertisementView mMainScrollUpAdvertisementView;
	private ArrayList<AdvertisementObject> mDataList = new ArrayList<AdvertisementObject>();

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

	/**
	 * 模拟网络获取数据
	 */
	private void initData() {
		AdvertisementObject advertisementObject = new AdvertisementObject();
		advertisementObject.title = "爆";
		advertisementObject.info = "踏青零食上京东,百万零食1元秒";
		mDataList.add(advertisementObject);
		advertisementObject = new AdvertisementObject();
		advertisementObject.title = "公告";
		advertisementObject.info = "看老刘中国行,满129减50!";
		mDataList.add(advertisementObject);
		advertisementObject = new AdvertisementObject();
		advertisementObject.title = "爆";
		advertisementObject.info = "高姿CC霜全渠道新品首发,领券199减50,点击查看";
		mDataList.add(advertisementObject);
	}

	private void initViews() {
		mMainScrollUpAdvertisementView = (MainScrollUpAdvertisementView) findViewById(R.id.main_advertisement_view);
		mMainScrollUpAdvertisementView.setData(mDataList);
		mMainScrollUpAdvertisementView.setTextSize(15);
		mMainScrollUpAdvertisementView
				.setOnItemClickListener(new OnItemClickListener() {

					@Override
					public void onItemClick(int position) {
						Toast.makeText(MainActivity.this,
								"点击了第" + position + "个广告条", Toast.LENGTH_SHORT)
								.show();
					}
				});
		mMainScrollUpAdvertisementView.setTimer(2000);
		mMainScrollUpAdvertisementView.start();
		

	}

}

注册我们的控件,为控件添加数据通过setData方法添加网络获取到的数据。通过setTextSize设置广告条中文字的大小,通过setOnItemClickListener方法设置监听事件,通过setTimer方法设置滚动的间隔时间,最后通过start方法开启滚动。其中stop方法用于在Activity暂停或销毁时调用,上面暂未写出,请大家自行添加。

至此使用完毕,整体使用还是挺简单的。

##代码讲解

以下是对BaseAutoScrollUpTextView这个基类的说明,先贴出完整的源代码:

package com.example.autoscrollup.view.base;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.example.autoscrollup.R;

/**
 * <pre>
 * 京东快报 自动向上滚动的广告基类
 * 内部包含TextView的自动向上滚动
 * 
 * <pre>
 * @author 顾林海
 * 
 * @param <T>
 */
public abstract class BaseAutoScrollUpTextView<T> extends ListView implements
		AutoScrollData<T> {

	/**
	 * 数据源
	 */
	private ArrayList<T> mDataList = new ArrayList<T>();
	
	/**
	 * 字体大小
	 */
	private float mSize=16;

	/**
	 * 数据总数
	 */
	private int mMax;

	private int position = -1;

	/**
	 * 向上滚动距离
	 */
	private int scroll_Y;

	private int mScrollY;

	/**
	 * 适配器
	 */
	private AutoScrollAdapter mAutoScrollAdapter = new AutoScrollAdapter();

	/**
	 * 监听器
	 */
	private OnItemClickListener mOnItemClickListener;

	private long mTimer = 1000;

	private Context mContext;

	/**
	 * 获取高度
	 * 
	 * @return
	 */
	protected abstract int getAdertisementHeight();

	private Handler handler = new Handler();
	Runnable runnable = new Runnable() {
		@Override
		public void run() {
			// 开启轮播
			switchItem();
			handler.postDelayed(this, mTimer);
		}
	};

	public interface OnItemClickListener {
		public void onItemClick(int position);
	}

	public BaseAutoScrollUpTextView(Context context, AttributeSet attrs,
			int defStyle) {
		super(context, attrs, defStyle);
		this.mContext = context;
		mScrollY = dip2px(getAdertisementHeight());
		init();

	}

	public BaseAutoScrollUpTextView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public BaseAutoScrollUpTextView(Context context) {
		this(context, null);
	}

	private void init() {
		this.setDivider(null);
		this.setFastScrollEnabled(false);
		this.setDividerHeight(0);
		this.setEnabled(false);
	}

	/**
	 * dp-->px
	 * 
	 * @param dipValue
	 * @return
	 */
	private int dip2px(float dipValue) {
		final float scale = mContext.getResources().getDisplayMetrics().density;
		return (int) (dipValue * scale + 0.5f);
	}

	/**
	 * 开始轮播
	 */
	private void switchItem() {
		if (position == -1) {
			scroll_Y = 0;
		} else {
			scroll_Y = mScrollY;
		}
		smoothScrollBy(scroll_Y, 2000);
		setSelection(position);
		position++;
	}

	/**
	 * 广告条适配器
	 * 
	 * @author 顾林海
	 * 
	 */
	private class AutoScrollAdapter extends BaseAdapter {

		@Override
		public int getCount() {
			final int count = mDataList == null ? 0 : mDataList.size();
			return count > 1 ? Integer.MAX_VALUE : count;
		}

		@Override
		public Object getItem(int position) {
			return mDataList.get(position % mMax);
		}

		@Override
		public long getItemId(int position) {
			return position % mMax;
		}

		@Override
		public View getView(final int position, View convertView,
				ViewGroup parent) {
			ViewHolder viewHolder;
			if (null == convertView) {
				viewHolder = new ViewHolder();
				convertView = LayoutInflater.from(mContext).inflate(
						R.layout.item_layout, null);
				viewHolder.mTitleView = (TextView) convertView
						.findViewById(R.id.tv_title);
				viewHolder.mInfoView = (TextView) convertView
						.findViewById(R.id.tv_info);
				convertView.setTag(viewHolder);
			} else {
				viewHolder = (ViewHolder) convertView.getTag();
			}
			T data = mDataList.get(position % mMax);
			viewHolder.mTitleView
					.setLayoutParams(new RelativeLayout.LayoutParams(
							RelativeLayout.LayoutParams.WRAP_CONTENT,
							dip2px(getAdertisementHeight())));
			viewHolder.mTitleView.setTextSize(mSize);
			viewHolder.mInfoView.setTextSize(mSize);
			viewHolder.mTitleView.setText(getTextTitle(data));
			viewHolder.mInfoView.setText(getTextInfo(data));
			convertView.setOnClickListener(new OnClickListener() {

				@Override
				public void onClick(View v) {
					mOnItemClickListener.onItemClick(position % mMax);
				}
			});
			return convertView;
		}
	}

	static class ViewHolder {
		TextView mTitleView;// 标题
		TextView mInfoView;// 内容
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		return false;
	}

	/**
	 * 添加数据
	 * 
	 * @param _datas
	 */
	public void setData(ArrayList<T> _datas) {
		mDataList.clear();
		mDataList.addAll(_datas);
		mMax = mDataList == null ? 0 : mDataList.size();
		this.setAdapter(mAutoScrollAdapter);
		mAutoScrollAdapter.notifyDataSetChanged();
	}
	
	/**
	 * 设置文字大小
	 * @param size
	 */
	public void setTextSize(float _size){
		this.mSize=_size;
	}

	/**
	 * 设置监听事件
	 */
	public void setOnItemClickListener(OnItemClickListener _listener) {
		this.mOnItemClickListener = _listener;
	}

	/**
	 * 设置轮播间隔时间
	 * 
	 * @param _time
	 *            毫秒单位
	 */
	public void setTimer(long _time) {
		this.mTimer = _time;
	}

	/**
	 * 开启轮播
	 */
	public void start() {
		handler.postDelayed(runnable, 1000);
	}

	/**
	 * 关闭轮播
	 */
	public void stop() {
		handler.removeCallbacks(runnable);
	}

}

以上主要是通过Handler类自带的postDelyed实现一个定时的轮播,轮播时调用了ListView的:

smoothScrollBy(scroll_Y, 2000);
setSelection(position);

数据的回调主要是定义了一个范型接口,范型接口的具体实现延迟到子类实现,这样方便大家格局不同场景下定义不同的广告条。

最后的最后大家使用时一定要注意在xml中使用你自己定义的广告条(继承BaseAutoScrollUpTextView),高度一定要与子类getAdertisementHeight方法返回的值一致






如果大家喜欢本人的文章,请多多支持,项目地址在最下方




> 以下是完整的github项目地址,欢迎star,fork > github项目源码地址:[点击【项目源码】](https://github.com/LinhaiGu/ASUpAdvertisementArticle)
  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
如果想要实现一个类似于 Android 官方的 RecyclerView 控件,可以使用以下步骤: 1. 添加依赖库 在项目的 build.gradle 文件中添加以下依赖库: ```groovy implementation 'androidx.recyclerview:recyclerview:1.1.0' ``` 2. 创建布局文件 创建一个 RecyclerView 布局文件,例如: ```xml <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 3. 创建适配器 创建一个适配器来管理 RecyclerView 中的数据和视图。您可以扩展 `RecyclerView.Adapter` 类并实现以下方法: ```java public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> { private List<String> mData; public MyAdapter(List<String> data) { mData = data; } @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_layout, parent, false); return new MyViewHolder(view); } @Override public void onBindViewHolder(MyViewHolder holder, int position) { String item = mData.get(position); holder.textView.setText(item); } @Override public int getItemCount() { return mData.size(); } public static class MyViewHolder extends RecyclerView.ViewHolder { TextView textView; public MyViewHolder(View itemView) { super(itemView); textView = itemView.findViewById(R.id.textView); } } } ``` 4. 设置布局管理器和适配器 在 Activity 或 Fragment 中,您需要设置一个布局管理器和适配器来控制 RecyclerView。例如: ```java RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); List<String> data = new ArrayList<>(); data.add("Item 1"); data.add("Item 2"); data.add("Item 3"); MyAdapter adapter = new MyAdapter(data); recyclerView.setAdapter(adapter); ``` 5. 自定义滚动效果 如果您想要实现自定义的滚动效果,您可以使用 `RecyclerView.ItemDecoration` 类。例如,以下代码将在 RecyclerView 中添加一个分隔线: ```java public class DividerItemDecoration extends RecyclerView.ItemDecoration { private Drawable mDivider; public DividerItemDecoration(Context context) { mDivider = ContextCompat.getDrawable(context, R.drawable.divider); } @Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { int left = parent.getPaddingLeft(); int right = parent.getWidth() - parent.getPaddingRight(); int childCount = parent.getChildCount(); for (int i = 0; i < childCount; i++) { View child = parent.getChildAt(i); RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); int top = child.getBottom() + params.bottomMargin; int bottom = top + mDivider.getIntrinsicHeight(); mDivider.setBounds(left, top, right, bottom); mDivider.draw(c); } } } ``` 然后,您可以在 Activity 或 Fragment 中设置这个装饰器: ```java RecyclerView recyclerView = findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); List<String> data = new ArrayList<>(); data.add("Item 1"); data.add("Item 2"); data.add("Item 3"); MyAdapter adapter = new MyAdapter(data); recyclerView.setAdapter(adapter); recyclerView.addItemDecoration(new DividerItemDecoration(this)); ``` 这样,您就可以实现一个类似 Android 官方的 RecyclerView 控件了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值