編輯:關於Android編程
Android 跑馬燈已經有很多版本,從最基本的TextView,到重寫TextView使TextView取消焦點限制,還有重寫TextView利用ScrollTo方法寫的,基本都能滿足一般需要。然而在使用過程中,發現一些意外---有時會不播放,刷新線程還在繼續但就是不播放,最後在github上找到一個用動畫實現跑馬燈的項目(項目地址:https://github.com/ened/Android-MarqueeView,再次對作者表示感謝),改造了一番,總算ok了。以後再也不用為跑馬燈煩惱了。
特點:
1. 文字長短都有跑馬燈效果。
2. 可以控制速度
代碼:
package com.example.test_marquee; import android.content.Context; import android.graphics.Paint; import android.text.Editable; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; import android.view.animation.TranslateAnimation; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; /** * LinearLayout作為父View,必須有一個子TextView * * 利用動畫實現 */ public class MarqueeView extends LinearLayout { private static final int TEXTVIEW_VIRTUAL_WIDTH = 2000;/* TextView默認寬度 */ private static final int DEFAULT_SPEED = 35;/* 默認滾動速度 越大滾動越慢 */ private static final int DEFAULT_ANIMATION_PAUSE = 0;/* 出去動畫與進入動畫的時間間隔 */ private static final String TAG = MarqueeView.class.getSimpleName(); private TextView mTextField;/* 該跑馬燈的孫子View之TextView */ private ScrollView mScrollView;/* 該跑馬燈的子View之mScrollView */ private Animation mMoveTextOut = null;/* 作用於TextView的動畫 --出去 */ private Animation mMoveTextIn = null;/* 作用於TextView的動畫 --進入 */ private Paint mPaint; private int mSpeed = DEFAULT_SPEED; private int mAnimationPause = DEFAULT_ANIMATION_PAUSE; private Interpolator mInterpolator = new LinearInterpolator(); private Runnable mAnimationStartRunnable; /** 字符串之間的間隔 */ private String interval = " "; private String stringOfItem = ""; /** str+interval的長度 */ private float widthOfItem = 0; private float widthOfTextView; private String stringOfTextView = ""; private float startXOfOut = 0; private float endXOfOut = 0; private float startXOfIn = 0; private float endXOfIn = 0; public MarqueeView(Context context) { super(context); init(context); } public MarqueeView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public MarqueeView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { // init helper mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStrokeWidth(1); mPaint.setStrokeCap(Paint.Cap.ROUND); mInterpolator = new LinearInterpolator(); } // 當給子View分配位置和尺寸時調用。 @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); Logcat.d(TAG, "onLayout called!" + "changed: " + changed); if (getChildCount() == 0 || getChildCount() > 1) { throw new RuntimeException( "MarqueeView must have exactly one child element."); } // if (changed) { View v = getChildAt(0); if (!(v instanceof TextView)) { throw new RuntimeException( "The child view of this MarqueeView must be a TextView instance."); } initView(getContext()); mTextField.setText(mTextField.getText()); } } /** Starts the configured marquee effect. */ public void startMarquee() { Logcat.d(TAG, "startMarquee called"); startTextFieldAnimation(); } // 一旦開始動畫,動畫結束開始由監聽器負責。 private void startTextFieldAnimation() { mAnimationStartRunnable = new Runnable() { public void run() { mTextField.startAnimation(mMoveTextOut); } }; postDelayed(mAnimationStartRunnable, mAnimationPause); } /** * Disables the animations. */ public void reset() { if (mAnimationStartRunnable == null) return; removeCallbacks(mAnimationStartRunnable); mTextField.clearAnimation(); mMoveTextOut.reset(); mMoveTextIn.reset(); invalidate(); } private void prepareAnimation() { // Measure mPaint.setTextSize(mTextField.getTextSize()); mPaint.setTypeface(mTextField.getTypeface()); float mTextWidth = mPaint.measureText(mTextField.getText().toString()); float width = getMeasuredWidth(); startXOfOut = -(mTextWidth - width) % widthOfItem; endXOfOut = -mTextWidth + width; startXOfIn = -(mTextWidth - width) % widthOfItem; endXOfIn = -mTextWidth + width; final int duration = ((int) Math.abs(startXOfOut - endXOfOut) * mSpeed); if (BuildConfig.DEBUG) { Log.d(TAG, "(int) Math.abs(startXOfOut - endXOfOut) : " + (int) Math.abs(startXOfOut - endXOfOut)); Log.d(TAG, "mSpeed : " + mSpeed); Log.d(TAG, "startXOfOut : " + startXOfOut); Log.d(TAG, "endXOfOut : " + endXOfOut); Log.d(TAG, "startXOfIn : " + startXOfIn); Log.d(TAG, "endXOfIn : " + endXOfIn); Log.d(TAG, "duration : " + duration); } mMoveTextOut = new TranslateAnimation(startXOfOut, endXOfOut, 0, 0); mMoveTextOut.setDuration(duration); mMoveTextOut.setInterpolator(mInterpolator); mMoveTextOut.setFillAfter(true); mMoveTextIn = new TranslateAnimation(startXOfIn, endXOfIn, 0, 0); mMoveTextIn.setDuration(duration); mMoveTextIn.setStartOffset(mAnimationPause); mMoveTextIn.setInterpolator(mInterpolator); mMoveTextIn.setFillAfter(true); mMoveTextOut.setAnimationListener(new Animation.AnimationListener() { public void onAnimationStart(Animation animation) { } public void onAnimationEnd(Animation animation) { mTextField.startAnimation(mMoveTextIn); } public void onAnimationRepeat(Animation animation) { } }); mMoveTextIn.setAnimationListener(new Animation.AnimationListener() { public void onAnimationStart(Animation animation) { } public void onAnimationEnd(Animation animation) { startTextFieldAnimation(); } public void onAnimationRepeat(Animation animation) { } }); } /** 初始化子View */ private void initView(Context context) { // Scroll View LayoutParams sv1lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); sv1lp.gravity = Gravity.CENTER_HORIZONTAL; mScrollView = new ScrollView(context); // Scroll View 1 - Text Field mTextField = (TextView) getChildAt(0); removeView(mTextField); mScrollView.addView(mTextField, new ScrollView.LayoutParams( TEXTVIEW_VIRTUAL_WIDTH, LayoutParams.WRAP_CONTENT)); mTextField.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @Override public void afterTextChanged(Editable editable) { Logcat.d(TAG, "afterTextChanged called"); // 如果提供的字符串未被加工過,就先加工,否則就開始動畫 if (!stringOfTextView.equals(editable.toString())) { String str = editable.toString(); mPaint.setTextSize(mTextField.getTextSize()); mPaint.setTypeface(mTextField.getTypeface()); stringOfItem = str + interval; widthOfItem = mPaint.measureText(stringOfItem); stringOfTextView = stringOfItem; widthOfTextView = widthOfItem; while (widthOfTextView <= 2 * getMeasuredWidth()) { stringOfTextView += stringOfItem; widthOfTextView = mPaint.measureText(stringOfTextView); } Logcat.d(TAG, "string of TextView deal ok!###"); Logcat.d(TAG, "lengthOfll: " + getMeasuredWidth() + "###"); Logcat.d(TAG, "lengthOfTextView: " + widthOfTextView + "###"); Logcat.d(TAG, "CONTENT: " + stringOfTextView + "###"); // 設置起始 mTextField.setText(stringOfTextView); return; } reset(); prepareAnimation(); expandTextView(); post(new Runnable() { @Override public void run() { startMarquee(); } }); } }); addView(mScrollView, sv1lp); } private void expandTextView() { ViewGroup.LayoutParams lp = mTextField.getLayoutParams(); lp.width = (int) widthOfTextView + 5; mTextField.setLayoutParams(lp); } }
http://download.csdn.net/detail/u012587637/8219987
在這裡先看看效果圖: OK,有時候,在我們的項目中會要求TextView中文本有一部分的字體顏色不一樣,這時我們應該使用 SpannableStrin
在項目中,我們常常需要實現界面滑動切換的效果。例如,微信界面的左右滑動切換效果。那這種效果是怎麼實現的?今天我就帶大家簡單了解ViewPager,並通過實例來實現該效果。
本文站在巨人的肩膀上 自我感覺又進了一步而成。基於翔神的大作基礎之上寫的一個為RecyclerView添加HeaderView FooterView 的另一種解決方案,上
上一篇《Qt on Android: Qt Quick 之 Hello World 圖文詳解》我們已經分別在電腦和 Android 手機上運行了第一個 Qt Quick