需求中涉及到的广告栏变化千变万化,这里,我们综合取材,有了下面的这篇文章。
开始的时候,我们使用的是MarqueeView,继承的ViewFlipper,但是会有一些bug,比如刷新数据时的重叠阴影等等
后来,考虑到后期的开发可能出现的修改,采用了自定义View继承LinearLayout来展示,代码实现如下:
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
public class LimitScrollerView extends LinearLayout implements View.OnClickListener{
private String TAG = "LimitScrollerView";
private LinearLayout ll_content1, ll_content2;
private LinearLayout ll_now, ll_down;
private int limit;
private int durationTime;
private int periodTime;
private int scrollHeight;
private int dataIndex;
private boolean isCancel;
private boolean boundData;
private final int MSG_SETDATA = 1;
private final int MSG_SCROL = 2;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == MSG_SETDATA){
boundData(true);
}else if(msg.what == MSG_SCROL){
if(isCancel)
return;
startAnimation();
}
}
};
public LimitScrollerView(Context context) {
this(context, null);
}
public LimitScrollerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LimitScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs){
LayoutInflater.from(context).inflate(R.layout.limit_scroller, this, true);
ll_content1 = (LinearLayout) findViewById(R.id.ll_content1);
ll_content2 = (LinearLayout) findViewById(R.id.ll_content2);
ll_now = ll_content1;
ll_down = ll_content2;
if(attrs!=null){
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LimitScroller);
limit = ta.getInt(R.styleable.LimitScroller_limit, 1);
durationTime = ta.getInt(R.styleable.LimitScroller_durationTime, 1000);
periodTime = ta.getInt(R.styleable.LimitScroller_periodTime, 1000);
ta.recycle();
Log.v(TAG, "limit="+limit);
Log.v(TAG, "durationTime="+durationTime);
Log.v(TAG, "periodTime="+periodTime);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight()/2);
scrollHeight = getMeasuredHeight();
}
private void startAnimation(){
if(isCancel)
return;
Log.i(TAG, "滚动");
ObjectAnimator anim1 = ObjectAnimator.ofFloat(ll_now, "Y",ll_now.getY(), ll_now.getY()-scrollHeight);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(ll_down, "Y",ll_down.getY(), ll_down.getY()-scrollHeight);
AnimatorSet animSet = new AnimatorSet();
animSet.setDuration(durationTime);
animSet.playTogether(anim1, anim2);
animSet.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
ll_now.setY(scrollHeight);
ll_down.setY(0);
LinearLayout temp = ll_now;
ll_now = ll_down;
ll_down = temp;
boundData(false);
handler.removeMessages(MSG_SCROL);
if(isCancel) {
return;
}
handler.sendEmptyMessageDelayed(MSG_SCROL, periodTime);
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animSet.start();
}
/**
* 向容器中添加子条目
* @param first
*/
private void boundData(boolean first){
if(adapter==null || adapter.getCount()<=0)
return;
if(first){
boundData = true;
ll_now.removeAllViews();
for(int i = 0; i<limit; i++){
if(dataIndex>=adapter.getCount())
dataIndex = 0;
View view = adapter.getView(dataIndex);
view.setClickable(true);
view.setOnClickListener(this);
ll_now.addView(view);
dataIndex ++;
}
}
ll_down.removeAllViews();
for(int i = 0; i<limit; i++){
if(dataIndex>=adapter.getCount())
dataIndex = 0;
View view = adapter.getView(dataIndex);
view.setClickable(true);
view.setOnClickListener(this);
ll_down.addView(view);
dataIndex ++;
}
}
@Override
public void onClick(View v) {
if(clickListener!=null){
Object obj = v.getTag();
clickListener.onItemClick(obj);
}
}
public interface LimitScrollAdapter{
public int getCount();
public View getView(int index);
}
private LimitScrollAdapter adapter;
interface OnItemClickListener{
public void onItemClick(Object obj);
}
private OnItemClickListener clickListener;
/**********************public API 以下为暴露的接口***********************/
/**
* 1、设置数据适配器
* @param adapter
*/
public void setDataAdapter(LimitScrollAdapter adapter){
this.adapter = adapter;
handler.sendEmptyMessage(MSG_SETDATA);
}
/**
* 2、开始滚动
* 应该在两处调用此方法:
* ①、Activity.onStart()
* ②、MyLimitScrllAdapter.setDatas()
*/
public void startScroll(){
if(adapter==null||adapter.getCount()<=0)
return;
if(!boundData){
handler.sendEmptyMessage(MSG_SETDATA);
}
isCancel = false;
Log.e(TAG, "开始滚动");
handler.removeMessages(MSG_SCROL);
handler.sendEmptyMessageDelayed(MSG_SCROL, periodTime);
}
/**
* 3、停止滚动
* 当在Activity不可见时,在Activity.onStop()中调用
*/
public void cancel(){
isCancel = true;
Log.e(TAG, "停止滚动");
}
/**
* 4、设置条目点击事件
* @param listener
*/
public void setOnItemClickListener(OnItemClickListener listener){
this.clickListener = listener;
}
}
<?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="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_content1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<LinearLayout
android:id="@+id/ll_content2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</LinearLayout>
public class MyLimitScrollAdapter implements LimitScrollerView.LimitScrollAdapter{
private Context context;
private List<String> mLists;
public MyLimitScrollAdapter(List<String> mLists, Context context) {
this.context = context;
this.mLists = mLists;
}
@Override
public int getCount() {
return mLists == null ? 0:mLists.size();
}
@Override
public View getView(int index) {
View itemView = LayoutInflater.from(context).inflate(R.layout.limit_scroller_item, null, false);
TextView tv_text = (TextView)itemView.findViewById(R.id.tv_text);
tv_text.setText(mLists.get(index));
return itemView;
}
}
<?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="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:visibility="gone"
android:id="@+id/iv_icon"
android:layout_width="30dip"
android:layout_height="30dip"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dip"
android:paddingTop="3dip"
android:paddingBottom="3dip"
android:textColor="@color/black"
android:text="asdfasdfasdf"/>
</LinearLayout>
Step5:values/attrs.xml的代码
<resources>
<declare-styleable name="LimitScroller">
<attr name="limit" format="integer" />
<attr name="durationTime" format="integer" />
<attr name="periodTime" format="integer" />
</declare-styleable>
</resources>
<LinearLayout
android:orientation="horizontal"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<views.LimitScrollerView
android:id="@+id/limitScroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
openxu:limit="2"
openxu:durationTime="1200"
openxu:periodTime="2000"/>
</LinearLayout>
Step7:数据初始化
private Date d1,d2;
private long diff,year,days,hours,minutes,second;
private String mDiffTime="";
private String mTitle="";
private MyLimitScrollAdapter myScrollAdapter;
private List<String> info = new ArrayList<>();
private List<String> mNoticeTitle = new ArrayList<>();
private List<String> mNoticeTime = new ArrayList<>();
for(int i=0;i<10;i++){
mNoticeTitle.add("公告"+i);
mNoticeTime.add("2018-04-11 17:26:10");
}
由于实际需求的满足,我们的广告头部需要插入时间的判断,显示最新的几条数据(这里数据库第一次调用接口会返回一个serviceTime,即d1.getTime()),代码如下:
if (null!=info || info.size()>0){
info.clear();
initSetTimeData();
}else {
initSetTimeData();
}
private void initSetTimeData() {
if (mNoticeTitle.size()>0){
for (int i = 0; i < mNoticeTitle.size(); i++) {
try {
d1 = (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).parse(mNoticeTime.get(i));
d2 = new Date(System.currentTimeMillis());
diff = d2.getTime() - d1.getTime();
year = diff / (1000 * 60 * 60 * 24 * 365);
days = diff / (1000 * 60 * 60 * 24);
hours = (diff-days*(1000 * 60 * 60 * 24))/(1000* 60 * 60);
minutes = (diff-days*(1000 * 60 * 60 * 24)-hours*(1000* 60 * 60))/(1000* 60);
second = (diff-days*(1000 * 60 * 60 * 24)-hours*(1000* 60 * 60)-minutes*(1000* 60))/(1000);
if (year>=1){
mDiffTime = year +"年前";
}else if (days>=1){
mDiffTime = days +"天前";
}else if (hours>=1 && hours<=24){
mDiffTime =hours+"小时前";
}else if (minutes>=1 && minutes<=60){
mDiffTime = minutes+"分钟前";
}else if (second>0 && second<=60){
mDiffTime = second+"秒前";
}else {
mDiffTime = "";
}
} catch (ParseException e) {
e.printStackTrace();
}
if (null!= mNoticeTitle.get(i)){
mTitle = mNoticeTitle.get(i);
}else {
mTitle = "";
}
info.add(mDiffTime + " "+ mTitle);
}
}else {
info.add(mDiffTime + " "+ mTitle);
}
}
myScrollAdapter = new MyLimitScrollAdapter(info,getActivity());
limitScroll.setDataAdapter(myScrollAdapter);
limitScroll.startScroll();
@Override
public void onStart() {
super.onStart();
limitScroll.startScroll();
}
@Override
public void onStop() {
super.onStop();
limitScroll.cancel();
}
最后祝你顺利完成!
