Android之高仿京東APP首頁“京東快報”自動向上滾動的廣告條
##前言
上次在京東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)