纵向滚动通知

纵向滚动通知 垂直跑马灯

自定义控件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();
            }
        });
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章