修改历史

2016.01.20
如果多次setNoticeList,可能会出现重复。那是因为setNoticeList的时候没有移除掉runnable,动画重复了的问题。不过也就那几秒钟有问题,等时间过后会自动回复正常。现在已经修复, Demo也已经重新上传了一份。
2016.03.16
1.20号的bug未成功修复,发现这是Android本身View动画的BUG,解决方法是将代码中的invisible改成gone,虽然还是有点小问题,但是已经无伤大雅。Android原生的TextSwitch控件也会出现相同的问题。博文中代码已修改,Demo就不重新上传了,自己修改吧

一、写在前面

很简单的一个小控件,项目刚好有这个需求,没有写的太好,动画能设置,能用就行,欢迎大家一起学习交流。
这是预览图,如果看到卡可能是因为制作成gif的原因。模拟器和真机很流畅。

二、使用方式

自定义属性

    <declare-styleable name="NoticeView">        <attr name="noticeTextSize" format="dimension"/>        <attr name="noticeTextColor" format="color|reference"/>    </declare-styleable>

布局文件中,随意使用。设置字体和颜色要自定义属性。

<com.aitsuki.balllayout.NoticeView        android:id="@+id/notice_view"        android:layout_width="match_parent"        android:layout_height="50dp"        android:paddingLeft="10dp"        android:paddingRight="10dp"        android:layout_gravity="center"        android:background="#4f00"        app:textSize="30dp"        app:textColor="#000">   </com.aitsuki.balllayout.NoticeView>

最后,在Activity中可以这么使用

public class MainActivity extends AppCompatActivity {    private NoticeView notice_view;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // 首先,模拟一个公告的集合。需要字符串泛型的list        final List<String> list = new ArrayList<>();        list.add("推荐歌曲:Eyelis - 絆にのせて");        list.add("挺好听的,我听了快100遍了");        list.add("好想回宿舍打游戏=。=");        // 然后设置进去。        notice_view = (NoticeView) findViewById(R.id.notice_view);        notice_view.setNoticeList(list);        // 这里可以设置一个动画集合,如果不想要动画可以设置成null        // 不过这里设置动画我设计的不太友好,需要的直接改源码可能更快捷。// notice_view.setEnterAnimation(null);// notice_view.setExitAnimation(null);        // 默认动画效果就是渐变和位移,可以通过这个设置动画的时长,默认是1000// notice_view.setAnimationDuration(1000);        // 公告切换一次是3秒,可以通过这个方法设置,设置的比动画的长就好。默认是3000// notice_view.setNoticeDuration(2000);        // 这里就是监听点击事件,TextView是点中的那个公告,position是位置。        // 比如点击之后想该条公告边灰色,就可以view.setTextColor();实现了        notice_view.setOnItemClickListener(new NoticeView.OnItemClickListener() {            @Override            public void onItemClick(TextView view, int position) {                String s = list.get(position);                view.setTextColor(Color.GRAY);                Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();            }        });    }    // 我们可以在这两个生命周期中启动和暂停公告的轮播,因为很多时候,公告可以点进其他Activity    // 我希望回到当前页面的时候,还可以看到这条公告在当前。    // 而且在其他页面的时候,这里没必要耗费资源去做动画。    @Override    protected void onResume() {        super.onResume();        notice_view.start();    }    @Override    protected void onPause() {        super.onPause();        notice_view.pause();    }}

三、代码实现

思路:
建一个类继承FrameLayout
1. 传入一个字符串集合,遍历字符串集合新建TextView。
2. 然后将TextView依次添加进FrameLayout
3. 控制TextView显示隐藏并做补间动画

以下是完整代码

package com.aitsuki.noticeviewdemo;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Paint;import android.os.Handler;import android.os.Looper;import android.text.TextPaint;import android.text.TextUtils;import android.util.AttributeSet;import android.util.TypedValue;import android.view.Gravity;import android.view.View;import android.view.animation.AlphaAnimation;import android.view.animation.AnimationSet;import android.view.animation.TranslateAnimation;import android.widget.FrameLayout;import android.widget.TextView;import java.util.ArrayList;import java.util.List;/** * Created by AItsuki on 2016/1/18. */public class NoticeView extends FrameLayout implements View.OnClickListener {    private final long DEFAULT_ANIMATION_DURATION = 1000; // 动画时长    private final long DEFAULT_NOTICE_SPACE = 3000; // 公告切换时长    private final int DEFAULT_TEXT_COLOR = 0xff000000;  // 默认字体颜色    private long mNoticeDuration = DEFAULT_NOTICE_SPACE;    private int mTextColor = DEFAULT_TEXT_COLOR;    private float mTextSize;    private LayoutParams mLayoutParams;    private List<TextView> mNoticeList;    private int mCurrentNotice;    private AnimationSet mEnterAnimSet;    private AnimationSet mExitAnimSet;    private Handler mHandler = new Handler(Looper.getMainLooper());    private NoticeRunnable mNoticeRunnalbe;    private OnItemClickListener mListener;    private TextPaint textPaint;    private boolean mIsRunning; // 是否正已经start()    public NoticeView(Context context) {        this(context, null);    }    public NoticeView(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.NoticeView);        mTextColor = array.getColor(R.styleable.NoticeView_textColor, DEFAULT_TEXT_COLOR);        mTextSize = array.getDimension(R.styleable.NoticeView_textSize, mTextSize);        array.recycle();        // 初始化动画        createExitAnimation();        createEnterAnimation();        // 初始化一个画笔,用于测量高度        textPaint = new TextPaint();    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        // 如果是未指定大小,那么设置宽为300px        int exceptWidth = 300;        int exceptHeight = 0;        // 计算高度,如果将高度设置为textSize会很丑,因为文字有默认的上下边距。        if(MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {            if(mTextSize > 0) {                textPaint.setTextSize(mTextSize);                Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();                exceptHeight = (int) (fontMetrics.bottom - fontMetrics.top);            }        }        int width = resolveSize(exceptWidth, widthMeasureSpec);        int height = resolveSize(exceptHeight, heightMeasureSpec);        setMeasuredDimension(width, height);    }    private void createEnterAnimation() {        mEnterAnimSet = new AnimationSet(false);        TranslateAnimation translateAnimation =                new TranslateAnimation(0,0,0,0, TranslateAnimation.RELATIVE_TO_PARENT, 1f,                        TranslateAnimation.RELATIVE_TO_SELF, 0f);        AlphaAnimation alphaAnimation = new AlphaAnimation(0f,1f);        mEnterAnimSet.addAnimation(translateAnimation);        mEnterAnimSet.addAnimation(alphaAnimation);        mEnterAnimSet.setDuration(DEFAULT_ANIMATION_DURATION);    }    private void createExitAnimation() {        mExitAnimSet = new AnimationSet(false);        TranslateAnimation translateAnimation =                new TranslateAnimation(0,0,0,0, TranslateAnimation.RELATIVE_TO_SELF, 0f,                        TranslateAnimation.RELATIVE_TO_PARENT, -1f);        AlphaAnimation alphaAnimation = new AlphaAnimation(1f,0f);        mExitAnimSet.addAnimation(translateAnimation);        mExitAnimSet.addAnimation(alphaAnimation);        mExitAnimSet.setDuration(DEFAULT_ANIMATION_DURATION);    }    /** * 设置公告的集合 * @param list */    public void setNoticeList(List<String> list) {        // 设置集合的时候,要将上一次的集合清除。        if(list == null || list.size() ==0) {            return;        }        // 暂停轮播        pause();        // 移除所有公告        removeAllViews();        if(mNoticeList == null) {            mNoticeList = new ArrayList<>();        }        mNoticeList.clear();        // 创建TextView        for (int i=0; i< list.size(); i++) {            TextView textView = createTextView(list.get(i));            mNoticeList.add(textView);            addView(textView);        }        // 显示第一条公告        mCurrentNotice = 0;        mNoticeList.get(mCurrentNotice).setVisibility(VISIBLE);        // 启动轮播        start();    }    /** * 设置条目点击侦听 * @param listener */    public void setOnItemClickListener(OnItemClickListener listener) {        setOnClickListener(this);        mListener = listener;    }    /** * 设置公告的切换间隔 * @param duration */    public void setNoticeDuration(long duration) {        if(duration > 0) {            mNoticeDuration = duration;        }    }    /** * 设置默认动画的时长 * @param duration */    public void setAnimationDuration(long duration) {        if(duration > 0) {            if(mEnterAnimSet != null) {                mEnterAnimSet.setDuration(duration);            }            if(mExitAnimSet != null) {                mExitAnimSet.setDuration(duration);            }        }    }    /** * @param animation */    public void setEnterAnimation(AnimationSet animation) {        mEnterAnimSet = animation;    }    /** * 设置公告的退出动画 * @param animation */    public void setExitAnimation(AnimationSet animation) {        mExitAnimSet = animation;    }    /** * 开始循环播放公告 * 推荐和pause()配合在生命周期中使用 */    public void start() {        // 如果轮播正在运行中,不重复执行        if(mIsRunning) {            return;        }        if(mNoticeRunnalbe == null) {            mNoticeRunnalbe = new NoticeRunnable();        } else {            mHandler.removeCallbacks(mNoticeRunnalbe);        }        mHandler.postDelayed(mNoticeRunnalbe, mNoticeDuration);        mIsRunning = true;    }    /** * 暂停循环播放公告 * 推荐和start()配合在生命周期中使用 */    public void pause() {        // 如果轮播已经停止,不重复执行        if(!mIsRunning) {            return;        }        if(mNoticeRunnalbe!= null) {            mHandler.removeCallbacks(mNoticeRunnalbe);        }        mIsRunning = false;    }    /** * 当前是否正在轮播公告 * @return */    public boolean isRunning() {        return mIsRunning;    }    /** * TextView默认水平居中, singline, Gone * @param text * @return */    private TextView createTextView(String text) {        if (mLayoutParams == null) {            mLayoutParams = new LayoutParams(                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);            mLayoutParams.gravity = Gravity.CENTER_VERTICAL;        }        TextView textView = new TextView(getContext());        textView.setLayoutParams(mLayoutParams);        textView.setSingleLine();        textView.setEllipsize(TextUtils.TruncateAt.END);        textView.setTextColor(mTextColor);        textView.setVisibility(GONE);        textView.setText(text);        // 如果有设置字体大小,如果字体大小为null。        if (mTextSize > 0) {            textView.setTextSize(TypedValue.COMPLEX_UNIT_PX,mTextSize);        }        return textView;    }    @Override    public void onClick(View v) {        if(mListener != null && mNoticeList!= null && mNoticeList.size() >0) {            mListener.onItemClick(mNoticeList.get(mCurrentNotice),mCurrentNotice);        }    }    /** * * 动画开始的一瞬间,就代表这个公告已经invisiable,下一个公告开始进入,点击事件也是给了下一个公告。 */    class NoticeRunnable implements Runnable {        @Override        public void run() {            // 隐藏当前的textView            TextView currentView = mNoticeList.get(mCurrentNotice);            currentView.setVisibility(GONE);            if(mExitAnimSet != null) {                currentView.startAnimation(mExitAnimSet);            }            mCurrentNotice++;            if(mCurrentNotice >= mNoticeList.size()) {                mCurrentNotice = 0;            }            // 显示下一个TextView            TextView nextView = mNoticeList.get(mCurrentNotice);            nextView.setVisibility(VISIBLE);            if(mEnterAnimSet != null) {                nextView.startAnimation(mEnterAnimSet);            }            mHandler.postDelayed(this, mNoticeDuration);        }    }    /** * 点击的回调。TextView是被点中的公告。 */    public interface OnItemClickListener {        void onItemClick(TextView view, int position);    }}

四、写在后面

非常简单的控件,适合初学者学习_(:з」∠)_
不过这种实现方式不好控制动画,并且补间动画的效果也比较一般。博主表示思考不到3秒就着手写了,所以也就这样了,如果想要更炫酷的动画可以换成属性动画。

话说csdn上传资源后就无法编辑和删除了,只能再上传一份,以后我还是上传到github吧。
这里是Demo下载地址:http://download.csdn.net/detail/u010386612/9411357

更多相关文章

  1. Android(安卓)activity启动关闭时滑动出现消失(并解决activity跳
  2. android矢量动画
  3. Android(安卓)SlidingMenu 开源项目 侧拉菜单的使用(详细配置)
  4. Android(安卓)跳转权限设置界面的终极适配(适配各大定制 ROM)
  5. 基于XML的android property animation
  6. android studio设置Tab为四空格缩进
  7. Matrix用法
  8. 当你的Android(安卓)Studio 设置No proxy不起作用时,该怎么做?
  9. android 系统权限大全

随机推荐

  1. android 开发故障记录
  2. Android学习点点滴滴之获取正在运行的进
  3. Android(安卓)的网络编程(4)-HttpClient
  4. [置顶] Android服务器推送之GCM
  5. Tiny210 从源代码开始创建Android
  6. Android入门进阶教程(20)-ContentProvide
  7. Android(安卓)之 WebView的使用介绍
  8. butterknife使用方法
  9. android微信开放平台,申请移动应用的应用
  10. android permission中英文大全