縱向滾動通知

縱向滾動通知 垂直跑馬燈

自定義控件ScrollNotification(extends ViewFlipper)

ViewFlipper官方解釋:Simple ViewAnimator that will animate between two or more views that have been added to it. Only one child is shown at a time. If requested, can automatically flip between each child at a regular interval. 是一個簡單的ViewAnimator,可以使添加進去的views動起來,一次只展示一個子view,可以設置固定時間間隔實現每個子view之間的自動切換。

實現思路:1.根據list向ViewFliper動態添加TextView作爲子view;2.利用ViewFliper的setInAnimation()、setOutAnimation()指定進入進出動畫來實現垂直滾動效果。

效果:

1.自定義xml屬性(Values/attrs.xml)

問題描述:允許通過layout佈局文件直接指定滾動通知內tTextView的TextSize、TextColor、SingleLine、Gravity屬性,但是ViewFlipper沒有上述屬性,需要自定義xml屬性。
實現方案:在項目values文件夾下創建attrs.xml文件(文件名隨意),根標籤爲resources,styleable名字爲ScrollNotification(一般使用控件名稱)。在這個名字裏可以定義多個屬性,例如定義了名爲mvTextSize的屬性,格式爲dimension|reference,意爲只能使用sp、dp、px、或引用R.dimens文件。其他自定義屬性類似。

<resources>
    <declare-styleable name="ScrollNotification">
        <attr name="mvInterval" format="integer|reference"/>
        <attr name="mvTextSize" format="dimension|reference"/>
        <attr name="mvTextColor" format="color|reference"/>
        <attr name="mvSingleLine" format="boolean"/>
        <attr name="mvGravity">
            <enum name="left" value="0"/>
            <enum name="center" value="1"/>
            <enum name="right" value="2"/>
        </attr>
    </declare-styleable>
</resources>

這裏的自定義屬性的format,可以有很多種:
- reference:attr的值只能指向某一資源的ID,例如取值@id/textView。
- string:表示attr是字符串類型。
- color:attr是顏色類型,例如#ff0000,也可以使用一個指向Color的資源,比如@android:color/background_dark,但是不能用0xffff0000這樣的值。
- dimension:attr是尺寸類型,例如取值16px、16dp,也可以使用一個指向類型的資源,比如@android:dimen/app_icon_size。
- boolean:表示attr是布爾類型的值,取值只能是true或false。
- integer:表示attr是整數類型,取值只能是整數,不能是浮點數。
- float:attr是浮點數類型,取值只能是浮點數或整數。
- fraction:表示attr是百分數類型,取值只能以%結尾,例如30%、120.5%等。
- enum:表示attr是枚舉類型,在定義enum類型的attr時,可以將attr的format設置爲enum,也可以不用設置attr的format屬性,但是必須在attr節點下面添加一個或多個enum節點,這樣取值就只能去enum裏定義的值了。
- flag:表示attr是bit位標記,flag與enum有相似之處,定義了flag的attr,在設置值時,可以通過|設置多個值,而且每個值都對應一個bit位,這樣通過按位或操作符|可以將多個值合成一個值,一般在用flag表示某個字段支持多個特性。使用flag類型時,不要設置attr的format的屬性,直接在attr節點下面添加flag節點。


2.在layout佈局文件中使用

- 1.在根標籤中添加新的命名空間

爲什麼要添加新的命名空間:觀察一下layout佈局文件中各個屬性是以android:開頭的,例如android:layout_width=”match_parent”android:是命名空間,根標籤最外層有一個xmlns:android=”http://schemas.android.com/apk/res/android”,xmlns(xmlNameSpace)的前綴爲android,也就是說所有以android:開頭的屬性都是在xmlns:android=”http://schemas.android.com/apk/res/android”命名空間之下的。要使用在步驟1中的自定義屬性,需要一個命名空間:

<1>app爲任意名稱,看個人意願,使用app爲命名空間時,直接在控件裏使用自定義屬性,eclipse會自動添加該命名空間;
<2>http://schemas.android.com/apk/res/是固定的;
<3>com.tiddler.testdemo爲當前應用包名。在eclipse環境下,可以使用當前應用包名;但是在Android Studio中因爲使用Gradle進行build,而Gradle不允許自定義的命名空間以包名結尾,在Android Studio中可以這樣定義命名空間xmlns:app=”http://schemas.android.com/apk/res-auto”,這樣定義的命名空間自動指向當前App的命名空間。):

- 2.在layout佈局文件中使用自定義控件,並使用自定義屬性

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.tiddler.demo"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#cc18A15F"
        android:orientation="horizontal"
        android:paddingLeft="10dp"
        android:paddingRight="10dp" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher" />

        <com.tiddler.myview.ScrollNotification
            android:id="@+id/scrollNotification"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_vertical"
            android:flipInterval="2000"
            app:mvSingleLine="true"
            app:mvTextColor="#FFFFFF"
            app:mvTextSize="8sp" 
            app:mvGravity="left"/>
    </LinearLayout>

</RelativeLayout>

3.自定義ScrollNotification.java類

- 1.聲明成員變量-數據列表,並實現get()、set()方法,以便使用代碼指定數據

    private Context mContext;
    private List<String> notices;// 數據列表

    public List<String> getNotices() {
        return notices;
    }

    public void setNotices(List<String> notices) {
        this.notices = notices;
    }

- 2.創建init()方法,將layout文件中的屬性值讀出來,賦給成員變量

// 聲明各個自定義屬性對應的成員變量,並設置默認值
    private int textSize = 6;// text字體大小
    private int textColor = 0x000000;// text顏色
    private boolean singleLine = false;// 是否單行顯示
    private int gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
    private static final int TEXT_GRAVITY_LEFT = 0, TEXT_GRAVITY_CENTER = 1,
            TEXT_GRAVITY_RIGHT = 2;

private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        this.mContext = context;
        if (notices == null) {
            notices = new ArrayList<>();
        }
        //通過Context的obtainStyledAttributes方法獲取TypedArray對象
        /*
         * 其中,TypedArray實例是個屬性的容器。AttributeSet是節點的屬性集合,在本例中是<com.tiddler.testdemo
         * .myview.ScrollNotificationa節點中的屬性集合。
         */
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
                R.styleable.ScrollNotification, defStyleAttr, 0);
        //在程序中獲取Layout文件中各個自定義屬性的賦值,如果沒有,則使用默認的值。
        singleLine = typedArray.getBoolean(
                R.styleable.ScrollNotification_mvSingleLine, false);
        if (typedArray.hasValue(R.styleable.ScrollNotification_mvTextSize)) {
            /*
             * getDimension如果是dimen是dp或sp的單位,將其乘以density,如果是px,則不乘,返回類型爲float。
             * getDimensionPixelSize總體類似,但是當單位是px時,也會乘以density,返回類型爲int。
             */
            textSize = (int) typedArray.getDimensionPixelSize(
                    R.styleable.ScrollNotification_mvTextSize, textSize);
        }
        textColor = typedArray.getColor(
                R.styleable.ScrollNotification_mvTextColor, textColor);
        int gravityType = typedArray.getInt(
                R.styleable.ScrollNotification_mvGravity, TEXT_GRAVITY_LEFT);
        switch (gravityType) {
        case TEXT_GRAVITY_CENTER:
            gravity = Gravity.CENTER;
            break;
        case TEXT_GRAVITY_RIGHT:
            gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
            break;
        }
        //在使用完typedArray之後,要調用recycle方法回收資源
        typedArray.recycle();
        //設置動畫
        setInAnimation(mContext, R.anim.in_scroll_notification);
        setOutAnimation(mContext, R.anim.out_scroll_notification);
    }

in_scroll_notification.xml文件

<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:duration="500"
        android:fromYDelta="100.0%p"
        android:toYDelta="0.0" />

    <alpha
        android:duration="500"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />

</set>

out_scroll_notification.xml文件

<set xmlns:android="http://schemas.android.com/apk/res/android">

   <translate
        android:duration="500"
        android:fromYDelta="0.0"
        android:toYDelta="-100.0%p" />

    <alpha
        android:duration="500"
        android:fromAlpha="1.0"
        android:toAlpha="0.0" />


</set>

- 3.在構造函數中調用init()方法

public ScrollNotification(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);

    }

-4.創建createTextView(String text, int position),並將自定義屬性裏設置的值設置到TextView

 /**
  * 創建ViewFlipper下的TextView
  */
    private TextView createTextView(String text, int position) {
        TextView tv = new TextView(mContext);
        tv.setGravity(gravity);
        tv.setText(text);
        tv.setTextColor(textColor);
        tv.setTextSize(textSize);
        tv.setSingleLine(singleLine);
        tv.setTag(position);
        return tv;
    }

- 5.創建start(),併爲子View添加點擊事件

private OnItemClickListener onItemClickListener;

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

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

 /** 啓動輪播 */
    public boolean start() {
        if (notices == null || notices.size() == 0){
            return false;
        } 
        removeAllViews();
        for (int i = 0; i < notices.size(); i++) {
            final TextView textView = createTextView(notices.get(i), i);
            final int finalI = i;
            textView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (onItemClickListener != null) {
                      onItemClickListener.onItemClick(finalI, textView);
                  }

                }
            });
            addView(textView);
        }
        if (notices.size() > 1) {
            startFlipping();
        }
        return true;
    }

- 6.創建startWithList(List notices)用以在ui線程調用

// 根據公告字符串列表啓動輪播
    public void startWithList(List<String> notices) {
        setNotices(notices);
        start();
    }

4.在Activity中使用ScrollNotification

private ScrollNotification mScrollNotification;

List<String> notices = new ArrayList<>();
        notices.add("測試數據11111111");
        notices.add("測試數據22222222");
        notices.add("測試數據33333333");
        notices.add("測試數據4444444");
        notices.add("測試數據5555555");
        notices.add("測試數據6666666");

mScrollNotification = (ScrollNotification) findViewById(R.id.scrollNotification);
        mScrollNotification.startWithList(notices);
        mScrollNotification.setOnItemClickListener(new ScrollNotification.OnItemClickListener() {

            @Override
            public void onItemClick(int position, TextView textView) {
                // TODO Auto-generated method stub
                Toast.makeText(getApplicationContext(), textView.getText().toString(), 0).show();
            }
        });
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章