1. 程式人生 > >android高仿京東快報(垂直迴圈滾動新聞欄)

android高仿京東快報(垂直迴圈滾動新聞欄)

京東的垂直滾動新聞欄的實現原理:

就是一個自定義的LinearLayout,並且textView能夠迴圈垂直滾動,而且條目可以點選,顯示區域最多顯示2個條目,並且還有交替的屬性垂直移動的動畫效果,通過執行緒來控制滾動的實現。

不多說看效果:
jd

程式碼實現

我們先來為控制元件設定自定義屬性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="JDAdverView">
        <attr name="gap" format
="integer" />
<attr name="animDuration" format="integer"/> </declare-styleable> </resources>

自定義控制元件的獲取屬性方法都一樣:

   //獲取自定義屬性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.JDAdverView);
        mAdverHeight = TypedValue.applyDimension(TypedValue.COMPLEX
_UNIT_DIP, jdAdverHeight, getResources().getDisplayMetrics()); int gap = array.getInteger(R.styleable.JDAdverView_gap, mGap); int animDuration = array.getInteger(R.styleable.JDAdverView_animDuration, mAnimDuration); //關閉清空TypedArray,防止記憶體洩露 array.recycle();

然後呢,我們來看一下條目的佈局:

<?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="60dp"
    android:background="#ffffff"
    android:gravity="center_vertical"
    android:orientation="horizontal">
    <TextView
        android:id="@+id/tag"
        android:textColor="#ff0000"
        android:layout_marginLeft="10dp"
        android:text="最新"
        android:background="@drawable/corner"
        android:textSize="18sp"
        android:padding="5dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/title"
        android:layout_marginLeft="10dp"
        android:singleLine="true"
        android:ellipsize="end"
        android:textSize="20sp"
        android:text="價格驚呆!電信千兆光纖上市"
        android:textColor="#000000"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

佈局很簡單,效果呢:
這裡寫圖片描述
不解釋,我們來寫介面卡了:

package com.example.jdadvernotice;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.example.jdadvernotice.entity.AdverNotice;
import com.example.jdadvernotice.view.JDAdverView;
import java.util.List;
/**
 * Created by Administrator on 2016/3/20.
 * 京東廣告欄資料介面卡
 *
 */

public class JDViewAdapter {
    private List<AdverNotice> mDatas;
    public JDViewAdapter(List<AdverNotice> mDatas) {
        this.mDatas = mDatas;
        if (mDatas == null || mDatas.isEmpty()) {
            throw new RuntimeException("nothing to show");
        }
    }
    /**
     * 獲取資料的條數
     * @return
     */
    public int getCount() {
        return mDatas == null ? 0 : mDatas.size();
    }

    /**
     * 獲取摸個數據
     * @param position
     * @return
     */
    public AdverNotice getItem(int position) {
        return mDatas.get(position);
    }
    /**
     * 獲取條目佈局
     * @param parent
     * @return
     */
    public View getView(JDAdverView parent) {
        return LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
    }

    /**
     * 條目資料適配
     * @param view
     * @param data
     */
    public void setItem(final View view, final AdverNotice data) {
        TextView tv = (TextView) view.findViewById(R.id.title);
        tv.setText(data.title);
        TextView tag = (TextView) view.findViewById(R.id.tag);
        tag.setText(data.url);
        //你可以增加點選事件
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //比如開啟url
                Toast.makeText(view.getContext(), data.url, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

然後我們就來自定義view:

package com.example.jdadvernotice.view;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
import com.example.jdadvernotice.JDViewAdapter;
import com.example.jdadvernotice.R;
/**
 * Created by zengyu on 2016/3/20.
 */
public class JDAdverView extends LinearLayout {
    //控制元件高度
    private float mAdverHeight = 0f;
    //間隔時間
    private final int mGap = 4000;
    //動畫間隔時間
    private final int mAnimDuration = 1000;
    //顯示文字的尺寸
    private final float TEXTSIZE = 20f;
    private JDViewAdapter mAdapter;
    private final float jdAdverHeight = 50;
    //顯示的view
    private View mFirstView;
    private View mSecondView;
    //播放的下標
    private int mPosition;
    //執行緒的標識
    private boolean isStarted;
    //畫筆
    private Paint mPaint;

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

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

    public JDAdverView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    /**
     * 初始化屬性
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        //設定為垂直方向
        setOrientation(VERTICAL);
        //抗鋸齒效果
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //獲取自定義屬性
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.JDAdverView);
        mAdverHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, jdAdverHeight, getResources().getDisplayMetrics());
        int gap = array.getInteger(R.styleable.JDAdverView_gap, mGap);
        int animDuration = array.getInteger(R.styleable.JDAdverView_animDuration, mAnimDuration);

        if (mGap <= mAnimDuration) {
            gap = mGap;
            animDuration = mAnimDuration;
        }
        //關閉清空TypedArray
        array.recycle();
    }

    /**
     * 設定資料
     */
    public void setAdapter(JDViewAdapter adapter) {
        this.mAdapter = adapter;
        setupAdapter();
    }

    /**
     * 開啟執行緒
     */
    public void start() {

        if (!isStarted && mAdapter.getCount() > 1) {
            isStarted = true;
            postDelayed(mRunnable, mGap);//間隔mgap重新整理一次UI
        }
    }

    /**
     * 暫停滾動
     */
    public void stop() {
        //移除handle更新
        removeCallbacks(mRunnable);
        //暫停執行緒
        isStarted = false;
    }
    /**
     * 設定資料適配
     */
    private void setupAdapter() {
        //移除所有view
        removeAllViews();
        //只有一條資料,不滾東
        if (mAdapter.getCount() == 1) {
            mFirstView = mAdapter.getView(this);
            mAdapter.setItem(mFirstView, mAdapter.getItem(0));
            addView(mFirstView);
        } else {
            //多個數據
            mFirstView = mAdapter.getView(this);
            mSecondView = mAdapter.getView(this);
            mAdapter.setItem(mFirstView, mAdapter.getItem(0));
            mAdapter.setItem(mSecondView, mAdapter.getItem(1));
            //把2個新增到此控制元件裡
            addView(mFirstView);
            addView(mSecondView);
            mPosition = 1;
            isStarted = false;
        }
    }
    /**
     * 測量控制元件的寬高
     *
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (LayoutParams.WRAP_CONTENT == getLayoutParams().height) {
            getLayoutParams().height = (int) mAdverHeight;
        } else {
            mAdverHeight = getHeight();
        }

        if (mFirstView != null) {
            mFirstView.getLayoutParams().height = (int) mAdverHeight;
        }
        if (mSecondView != null) {
            mSecondView.getLayoutParams().height = (int) mAdverHeight;
        }
    }

    /**
     * 畫布局
     *
     * @param canvas
     */
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        mPaint.setColor(Color.WHITE);
mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, TEXTSIZE, getResources().getDisplayMetrics()));
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawText("瑞士維氏軍刀", TEXTSIZE, getHeight() * 2 / 3, mPaint);//寫文字2/3的高度
    }
    /**
     * 垂直滾蛋
     */
    private void performSwitch() {
        //屬性動畫控制控制元件滾動,y軸方向移動
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(mFirstView, "translationY", mFirstView.getTranslationY() - mAdverHeight);
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(mSecondView, "translationY", mSecondView.getTranslationY() - mAdverHeight);
        //動畫集
        AnimatorSet set = new AnimatorSet();
        set.playTogether(animator1, animator2);//2個動畫一起
        set.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {//動畫結束
                mFirstView.setTranslationY(0);
                mSecondView.setTranslationY(0);
                View removedView = getChildAt(0);//獲得第一個子佈局
                mPosition++;
                //設定顯示的佈局
                mAdapter.setItem(removedView, mAdapter.getItem(mPosition % mAdapter.getCount()));
                //移除前一個view
                removeView(removedView);
                //新增下一個view
                addView(removedView, 1);
            }
        });
        set.setDuration(mAnimDuration);//持續時間
        set.start();//開啟動畫
    }
    private AnimRunnable mRunnable = new AnimRunnable();
    private class AnimRunnable implements Runnable {
        @Override
        public void run() {
            performSwitch();
            postDelayed(this, mGap);
        }
    }

    /**
     * 銷燬View的時候呼叫
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        //停止滾動
        stop();
    }
    /**
     * 螢幕 旋轉
     *
     * @param newConfig
     */
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
    }
}

從上面可以看出,控制元件最多可以顯示2個條目,並且用執行緒控制,根據條目的下標輪流滾動顯示。

具體使用程式碼:

初始化資料:

private void initData() {
        datas.add(new AdverNotice("瑞士維氏軍刀 新品滿200-50","最新"));
        datas.add(new AdverNotice("家居家裝煥新季,講199減100!","最火爆"));
        datas.add(new AdverNotice("帶上相機去春遊,尼康低至477","HOT"));
        datas.add(new AdverNotice("價格驚呆!電信千兆光纖上市","new"));
    }

繫結介面卡開啟滾動執行緒:

        initData();
        final JDViewAdapter adapter = new JDViewAdapter(datas);
        final JDAdverView tbView = (JDAdverView) findViewById(R.id.jdadver);
        tbView.setAdapter(adapter);
        //開啟執行緒滾東
        tbView.start();

就寫到這裡吧,很晚了睡覺,歡迎大家前來拍磚。
自定義JDViewdemo傳送門
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

新加ViewFlipper實現京東快報

看了大家的評論,然後覺得又學到了新的知識,這裡就用ViewFlipper來實現這個功能,哈哈,果斷很簡單。

來看下簡單的程式碼:

    <ViewFlipper
        android:id="@+id/flipper"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:autoStart="true"
        android:flipInterval="900"
        android:inAnimation="@anim/push_up_in"
        android:outAnimation="@anim/push_up_out" >
        <include layout="@layout/news_one" />
        <include layout="@layout/news_two" />
        <include layout="@layout/news_three" />
        <include layout="@layout/news_four"/>
    </ViewFlipper>

是不是很簡單利用autoStart 就不需要開執行緒了,flipInterval設定滾動的時間,inAnimation和outAnimation使用切入切出動畫。
傳送門:JDViewFlipper.rar

新加TextSwitcher實現京東快報

這也是第一使用TextSwitcher,再次感謝大家的幫助讓我學到了新的東西。

TextSwitcher集成了ViewSwitcher, 因此它具有與ViewSwitcher相同的特性:可以在切換View元件時使用動畫效果。與ImageSwitcher相似的是,使用TextSwitcher也需要設定一個ViewFactory。與ImageSwitcher不同的是,TextSwitcher所需要的ViewFactory的makeView()方法必須返回一個TextView元件。

TextSwitcher與TextView的功能有點類似,它們都可用於顯示文字內容,區別在於TextSwitcher的效果更炫,它可以指定文字切換時的動畫效果。現在控制元件的效果還是不錯的,inAnimation和outAnimation可以直接設定滾動動畫。

<?xml version="1.0" encoding="utf-8"?>
<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"
    android:background="#ffffff"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_centerInParent="true"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="10dp"
        android:background="@drawable/bg"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:paddingLeft="5dp"
            android:text="京東"
            android:textSize="16sp"
            android:textStyle="bold" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ff0000"
            android:padding="5dp"
            android:paddingLeft="5dp"
            android:text="快報"
            android:textColor="#ffffff"
            android:textSize="16sp"
            android:textStyle="bold" />

        <View
            android:layout_width="1dp"
            android:layout_height="50dp"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            android:background="#e0e0e0" />
        <!-- flipInterval可能是一次切換的時間 -->
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center_vertical"
            android:layout_gravity="center_vertical"
            android:orientation="horizontal">
               <TextSwitcher
                android:id="@+id/textSwitcher_tag"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_weight="3"
                android:padding="0dp"
                android:gravity="center_vertical"
                android:layout_gravity="center_vertical"
                android:inAnimation="@anim/push_up_in"
                android:outAnimation="@anim/push_up_out">  
                </TextSwitcher>
                <TextSwitcher
                android:id="@+id/textSwitcher_title"
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:layout_weight="1"
                android:padding="0dp"
                android:gravity="center_vertical"
                android:layout_gravity="center_vertical"
                android:inAnimation="@anim/push_up_in"
                android:outAnimation="@anim/push_up_out">    
                </TextSwitcher>
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

佈局也就是這麼簡單,然後呢為TextSwitcher設定ViewFactory,該TextSwitcher即可正常工作。唯一不足的是

textSwitcher.setFactory(new ViewFactory() {

      @Override
      public View makeView() {
        TextView tv = new TextView(context);
         ......
        return tv;
      }
    });

只能返回textView不能載入自己想要的控制元件。然後需要使用執行緒來滾動textSwitcher實現你想要的功能。
下面看具體程式碼:

package com.losileeya.jdtextswitcher;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.TypedValue;
import android.view.View;
import android.widget.TextSwitcher;
import android.widget.TextView;
import android.widget.ViewSwitcher;
public class MainActivity extends AppCompatActivity {
    private TextSwitcher textSwitcher_tag,textSwitcher_title;
    // 要顯示的文字
    String[] tags = new String[]
            {
                    "最新",
                    "最火爆",
                    "HOT",
                    "new"
            };
    // 要顯示的文字
    String[] titles = new String[]
            {
                    "瑞士維氏軍刀 新品滿200-50",
                    "家居家裝煥新季,講199減100!",
                    "帶上相機去春遊,尼康低至477",
                    "價格驚呆!電信千兆光纖上市"
            };
    private int curStr;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textSwitcher_tag= (TextSwitcher) findViewById(R.id.textSwitcher_tag);
        textSwitcher_title= (TextSwitcher) findViewById(R.id.textSwitcher_title);
        textSwitcher_title.setFactory(new ViewSwitcher.ViewFactory() {
            @Override
            public View makeView() {
                final TextView tv = new   
                TextView(MainActivity.this);
                tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
                tv.setPadding(20, 20, 20, 20);
                return tv;
            }
        });
        textSwitcher_tag.setFactory(new ViewSwitcher.ViewFactory() {
            @Override
            public View makeView() {
                final TextView tv = new  
                TextView(MainActivity.this);
                tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
                tv.setPadding(30, 20, 20,20);
                tv.setTextColor(Color.RED);
               tv.setBackgroundResource(R.drawable.corner);
                return tv;
            }
        });
        final Handler handler = new Handler();
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                textSwitcher_tag.setText(tags[curStr++ %    
                tags.length]);
                textSwitcher_title.setText(titles[curStr++   
                % titles.length]);
                handler.postDelayed(this, 1000);
            }
        }, 1000);
    }
}