BMoveView,RadioGroup添加移動的特效View(update)

話說BMoveView最初只是為了實現(xiàn)一個底部RadioButtton的移動動畫實現(xiàn)的效果,都是很久之前的一個自定義View了衫贬,現(xiàn)在重新修改了部分實現(xiàn),優(yōu)化了一些方法,支持更多的RadioButtton個數(shù)了辛孵,以前只支持三個,現(xiàn)在支持五赡磅、六魄缚、七、八焚廊、N個RadioButtton了冶匹,添加了動態(tài)實現(xiàn)方式。

動態(tài)圖還是以前的咆瘟。O(∩_∩)O

過時的上一篇鏈接 BMoveView,RadioGroup添加移動的特效View

RadioButton 除了變顏色,添加圖片顯示外嚼隘,我們還可以添加如下的特定效果。動畫可以增加APP的美感袒餐。

先上圖:

RadioButtton移動特效.gif

很多屬性可以自定義

我的github 源碼使用鏈接
BMoveView鏈接
很多的自定義View

歡迎點個Star
屬性 含義
circleColor 圓環(huán)的顏色
lineColor 下面的線條的顏色
lineDuration 線條頭的移動時間(單位ms)
lineWidth 線條的寬度
circleDuration 圓圈的動畫時間(單位ms)
circleCenterColor 圓圈中心的顏色(可以不和背景一樣)
circlemRadio 圓圈的半徑
buttonCount button個數(shù)

最后一個屬性是新添加的 buttonCount,可以設置button的個數(shù)飞蛹,也可以通過代碼設置

mBMoveView.setButonCount(4);

以上是所有的屬性,可以實現(xiàn)多個button的移動動畫灸眼。使用方法還是差不多卧檐。可以查看github連接

BMoveView github地址

在布局文件XML里

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:gravity="center"
    android:layout_height="match_parent"
    xmlns:yk="http://schemas.android.com/apk/res-auto"
    tools:context="com.yk.bmoveview.MainActivity">

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="60dp">
    <com.yk.bmoveview.BMoveView
        android:id="@+id/bmoveview"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        yk:circleColor="#fd4040"
        yk:lineColor="#fd4040"
        yk:lineDuration="800"
        yk:lineWidth="3"
        yk:circleDuration="500"
        yk:circleCenterColor="#FFFFFF"
        yk:circlemRadio="22"
        />
    <RadioGroup
        android:id="@+id/rg_group"
        android:gravity="center"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="60dp">
        <RadioButton
            android:id="@+id/rb_rec"
            android:button="@null"
            android:text="推薦"
            android:visibility="visible"
            android:layout_weight="1"
            android:textColor="@drawable/rb_button"
            android:textSize="18sp"
            android:gravity="center"
            android:layout_width="0dp"
            android:layout_height="match_parent"/>
        <RadioButton
            android:id="@+id/rb_first"
            android:button="@null"
            android:text="索引"
            android:layout_weight="1"
            android:textColor="@drawable/rb_button"
            android:textSize="18sp"
            android:gravity="center"
            android:layout_width="0dp"
            android:layout_height="match_parent"/>
        <RadioButton
            android:id="@+id/rb_second"
            android:button="@null"
            android:text="熱門"
            android:layout_weight="1"
            android:textColor="@drawable/rb_button"
            android:textSize="18sp"
            android:gravity="center"
            android:layout_width="0dp"
            android:layout_height="match_parent"/>
        <RadioButton
            android:id="@+id/rb_third"
            android:button="@null"
            android:text="我的"
            android:layout_weight="1"
            android:textColor="@drawable/rb_button"
            android:textSize="18sp"
            android:gravity="center"
            android:layout_width="0dp"
            android:layout_height="match_parent"/>
    </RadioGroup>
</RelativeLayout>  

BMoveView作為背景實現(xiàn)幢炸,添加RadioGroup泄隔,務必保證RadioButton的個數(shù)和我們設置的buttonCount保持一致,否者會出錯宛徊。

在Activity里,如下:

 public class MainActivity extends AppCompatActivity {

private int mFirstPos;
private int mLastPos;
private BMoveView mBMoveView;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    bMoveInit();
}

private void bMoveInit() {
    mBMoveView = (BMoveView) findViewById(R.id.bmoveview);
    RadioGroup mRadioGroup= (RadioGroup) findViewById(R.id.rg_group);
    ((RadioButton) (mRadioGroup.getChildAt(0))).setChecked(true);
    mFirstPos = 0;
    mBMoveView.setButonCount(4);
    mBMoveView.startAnim();
    mRadioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(RadioGroup group, int checkedId) {
            for (int i = 0; i < group.getChildCount(); i++) {
                boolean checked = ((RadioButton) (group.getChildAt(i))).isChecked();
                if(checked){
                    mLastPos = i;
                    mBMoveView.setTwoPos(mFirstPos, mLastPos);
                    mFirstPos = mLastPos;
                }
            }
        }
    });
}

}

主要是記錄兩次的位置,就能實現(xiàn)這個效果了佛嬉,使用起來并不是很復雜逻澳。其中關鍵方法為

mBMoveView.setTwoPos(mFirstPos, mLastPos);

設置兩次的位置,默認第一次設為0暖呕,即表示第一個位置在第一位斜做,也是默認的選中第一個。

使用方法講完了,下面介紹是如何實現(xiàn)的

主要是在onDraw里,繪制我們的view,分析動畫和過程

  • 一個圓圈的動畫,就是旋轉
  • 下面一個線條,添加移動效果
  • 線條移動頭和尾的移動時間不同
  • 移動的方向和位置

主要由以上四步驟實現(xiàn)湾揽,弧瓤逼,移動的線條,位置库物,方向

    /**
   * Created by yukun on 18-11-12.
   */
  public class BMoveView extends View {

  private int mWidth;
  private int mHeight;
  private Paint mPaint;
  private Paint mPaintLine;
  private RectF mRectF;
  private int mBoardWidth=50;
  private int firstPos;  //第一次點擊位置
  private int mRoationx=0;
  private int mRadio=5;
  private int position=0;//點擊到的button位置
  private int mLineEndLength;
  private int mLineLength;
  private int mCircleColor;
  private int mLineColor;
  private int mLineDuration;
  private int mLineWidth;
  private int mCircleDuration;
  private int mCircleCenterColor;
  private int mCirclemRadio;
  private int mButonCount;

  public BMoveView(Context context) {
    super(context);
    init(context,null,0);
  }

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

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

  private void init(Context context, AttributeSet attrs, int defStyleAttr) {

    TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BMoveView, defStyleAttr, 0);

    int n = a.getIndexCount();

    for (int i = 0; i < n; i++) {
        int attr = a.getIndex(i);
        switch (attr) {
            case R.styleable.BMoveView_circleColor:
                mCircleColor = a.getColor(attr, Color.WHITE);
                break;
            case R.styleable.BMoveView_lineColor:
                mLineColor = a.getColor(attr, Color.GRAY);
                break;
            case R.styleable.BMoveView_circleCenterColor:
                mCircleCenterColor = a.getColor(attr, Color.GRAY);
                break;
            case R.styleable.BMoveView_lineDuration:
                mLineDuration = a.getInt(attr,500);
                break;
            case R.styleable.BMoveView_lineWidth:
                mLineWidth = a.getInt(attr, 5);
                break;
            case R.styleable.BMoveView_circleDuration:
                mCircleDuration = a.getInt(attr,500);
                break;
            case R.styleable.BMoveView_circlemRadio:
                mCirclemRadio = a.getInt(attr,500);
                break;
            case R.styleable.BMoveView_buttonCount:
                mButonCount = a.getInt(attr,3);
                break;
        }
    }
    a.recycle();
    mBoardWidth=dip2px(context,mCirclemRadio);
    mRadio=dip2px(context,mLineWidth);
    mPaint=new Paint();
    mPaintLine = new Paint();
  }

    /**
   * 初始化第一次的位置
   * @param firstPos
   * @param lastPos
   */
  public void setTwoPos(int firstPos,int lastPos) {
    this.firstPos = firstPos;
    this.position=lastPos;
    this.mRoationx = 0;
    //動畫的方法 (lastPos-firstPos)兩次相減得到需要移動的距離
    leftToRigth(lastPos - firstPos);
  }
  /**
   * button個數(shù)
   * @param butonCount
   */

  public void setButonCount(int butonCount) {
    mButonCount = butonCount;
  }

  /**
   *
   * @param startLineLastPosition 正為向右,負為想左,如果是1.則跨度為一,如果是2,則跨度為2;
   */
  private void leftToRigth(int startLineLastPosition) {
    startAnim();
    startLineAnim(startLineLastPosition);
    startLineEndAnim(startLineLastPosition);
  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //畫弧度
    mPaint.setColor(mCircleColor);
    mPaint.setAntiAlias(true);
    mPaint.setStrokeWidth(mRadio);
    mPaint.setStyle(Paint.Style.STROKE);//只有邊
    //畫圓弧的矩形位置
    mRectF=new RectF(mWidth/(mButonCount*2)-mBoardWidth+position*mWidth/mButonCount,mHeight/2-mBoardWidth,mWidth/(mButonCount*2)+mBoardWidth+position*mWidth/mButonCount,mHeight/2+mBoardWidth);
    canvas.drawArc(mRectF,90,mRoationx,false,mPaint);
    //畫圓覆蓋
    mPaintLine.setColor(Color.BLUE);
    mPaintLine.setAntiAlias(true);
    mPaintLine.setStyle(Paint.Style.FILL);
    //可以畫內圓圈的顏色
  //        canvas.drawArc(mRectF,90,mRoationx,true,mPaintLine);
    //畫線條
    mPaintLine.setColor(mLineColor);
    mPaintLine.setStrokeWidth(mRadio);
    //起始和結束不同,每次動畫結束位置是相同的,控制起始點和結束點
    canvas.drawLine(mWidth/(mButonCount*2)+firstPos*mWidth/mButonCount+mLineEndLength,mHeight/2+mBoardWidth,mWidth/(mButonCount*2)+firstPos*mWidth/mButonCount+mLineLength,mHeight/2+mBoardWidth, mPaintLine);
  }

  //圓圈的動畫
  public void startAnim(){
    ValueAnimator animator = ValueAnimator.ofInt(0,360);
    animator.setDuration(mCircleDuration);
    animator.setStartDelay(mCircleDuration);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mRoationx = (int)animation.getAnimatedValue();
            postInvalidate();
        }
    });
    animator.start();
  }

  //線條開始的動畫
  private void startLineAnim(int startLineLastPosition){
    ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/mButonCount)*startLineLastPosition);
    animator.setDuration(mLineDuration);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mLineLength = (int)animation.getAnimatedValue();
            postInvalidate();
        }
    });
    animator.start();
  }

  //線條結束的動畫
  private void startLineEndAnim(int startLineLastPosition){
    ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/mButonCount)*startLineLastPosition);
    animator.setDuration(mCircleDuration);
    animator.setInterpolator(new LinearInterpolator());
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mLineEndLength = (int)animation.getAnimatedValue();
            postInvalidate();
        }
    });
    animator.start();
  }

  private static int dip2px(Context context, float dpValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
   }
  }

主要代碼如下

/**
 * @param startLineLastPosition 正為向右,負為想左,如果是1.則跨度為一,如果是2,則跨度為2;
 */
private void leftToRigth(int startLineLastPosition) {
    startAnim();
    startLineAnim(startLineLastPosition);
    startLineEndAnim(startLineLastPosition);
}

調用的方法

主要是傳遞過來的startLineLastPosition

//動畫的方法 (lastPos-firstPos)兩次相減得到需要移動的距離
 leftToRigth(lastPos - firstPos);

其中lastPos - firstPos可以得到我們需要移動的跨度是一個button還是兩個button的距離霸旗,其正負數(shù)表示我們的方向,這里對以前的方法做了簡化戚揭。具體的可以看動畫實現(xiàn)诱告。如下:

    //圓圈的動畫
 public void startAnim(){
 ValueAnimator animator = ValueAnimator.ofInt(0,360);
animator.setDuration(mCircleDuration);
animator.setStartDelay(mCircleDuration);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mRoationx = (int)animation.getAnimatedValue();
        postInvalidate();
    }
});
animator.start();
}

//線條開始的動畫
private void startLineAnim(int startLineLastPosition){
ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/mButonCount)*startLineLastPosition);
animator.setDuration(mLineDuration);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mLineLength = (int)animation.getAnimatedValue();
        postInvalidate();
    }
});
animator.start();
}

//線條結束的動畫
private void startLineEndAnim(int startLineLastPosition){
ValueAnimator animator = ValueAnimator.ofInt(0,(mWidth/mButonCount)*startLineLastPosition);
animator.setDuration(mCircleDuration);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        mLineEndLength = (int)animation.getAnimatedValue();
        postInvalidate();
    }
});
animator.start();
}

對于弧度的動畫,我們?yōu)槟阌涗浟嘶《扔?~360的弧度民晒,通過delay延遲得到畫弧度的實現(xiàn)精居,對于移動的線條,其實很容易就得到了最后的位置潜必,其中也記錄了之前的位置靴姿,為了實現(xiàn)動畫,通過兩個線條的參數(shù)磁滚,通過ValueAnimator動畫得到不同的延遲佛吓,顯示線條移動的軌跡。

下面是onDraw的實現(xiàn)恨旱,

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//畫弧度
mPaint.setColor(mCircleColor);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(mRadio);
mPaint.setStyle(Paint.Style.STROKE);//只有邊
//畫圓弧的矩形位置
mRectF=new RectF(mWidth/(mButonCount*2)-mBoardWidth+position*mWidth/mButonCount,mHeight/2-mBoardWidth,mWidth/(mButonCount*2)+mBoardWidth+position*mWidth/mButonCount,mHeight/2+mBoardWidth);
canvas.drawArc(mRectF,90,mRoationx,false,mPaint);
//畫圓覆蓋
 // mPaintLine.setColor(Color.BLUE);
  mPaintLine.setAntiAlias(true);
 mPaintLine.setStyle(Paint.Style.FILL);
//可以畫內圓圈的顏色
  //        canvas.drawArc(mRectF,90,mRoationx,true,mPaintLine);
  //畫線條
mPaintLine.setColor(mLineColor);
mPaintLine.setStrokeWidth(mRadio);
//起始和結束不同,每次動畫結束位置是相同的,控制起始點和結束點
canvas.drawLine(mWidth/(mButonCount*2)+firstPos*mWidth/mButonCount+mLineEndLength,mHeight/2+mBoardWidth,mWidth/(mButonCount*2)+firstPos*mWidth/mButonCount+mLineLength,mHeight/2+mBoardWidth, mPaintLine);
}

幾個變量控制辈毯,需要理解清除,一個是弧度的mRoationx,這個由0~360度的變化搜贤。mLineEndLengthmLineLength,分別是線條移動的時間差钝凶,得到移動的動畫效果仪芒。通過delay的連接,使它們連接起來耕陷,形成完整的動畫效果掂名。

------------------------- 特別提醒 -------------------------

解開注釋

 //畫圓覆蓋
  mPaintLine.setColor(Color.BLUE);
  mPaintLine.setAntiAlias(true);
 mPaintLine.setStyle(Paint.Style.FILL);
//可以畫內圓圈的顏色
 canvas.drawArc(mRectF,90,mRoationx,true,mPaintLine);

可以得到一些異形效果呃。????

S81112-16581323.jpg
S81112-16580879.jpg
S81112-16580527.jpg

gif圖太小,找點大圖鎮(zhèn)樓哟沫,看看完整的效果

S70427-19180549.jpg
S81112-15260812.jpg
S81112-15280658.jpg
S81112-15275978.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末饺蔑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子嗜诀,更是在濱河造成了極大的恐慌猾警,老刑警劉巖孔祸,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異发皿,居然都是意外死亡崔慧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門穴墅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惶室,“玉大人,你說我怎么就攤上這事玄货』食” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵松捉,是天一觀的道長夹界。 經常有香客問我,道長惩坑,這世上最難降的妖魔是什么掉盅? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮以舒,結果婚禮上趾痘,老公的妹妹穿的比我還像新娘。我一直安慰自己蔓钟,他們只是感情好永票,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滥沫,像睡著了一般侣集。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兰绣,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天世分,我揣著相機與錄音,去河邊找鬼缀辩。 笑死臭埋,一個胖子當著我的面吹牛,可吹牛的內容都是我干的臀玄。 我是一名探鬼主播瓢阴,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼健无!你這毒婦竟也來了荣恐?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎叠穆,沒想到半個月后少漆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡痹束,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年检疫,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祷嘶。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡屎媳,死狀恐怖,靈堂內的尸體忽然破棺而出论巍,到底是詐尸還是另有隱情烛谊,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布嘉汰,位于F島的核電站丹禀,受9級特大地震影響,放射性物質發(fā)生泄漏鞋怀。R本人自食惡果不足惜双泪,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望密似。 院中可真熱鬧焙矛,春花似錦、人聲如沸残腌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抛猫。三九已至蟆盹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闺金,已是汗流浹背逾滥。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留败匹,地道東北人匣距。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像哎壳,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尚卫,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

推薦閱讀更多精彩內容

  • 1归榕、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數(shù)據庫組件 SD...
    陽明先生_X自主閱讀 15,982評論 3 119
  • 很久沒有打開簡書了,大概是因為只有這個地方沒有認識的人吧吱涉,說話刹泄、抒發(fā)情緒外里、表達觀點會更自如自在一些√厥扣扣上有從小學...
    六月清泉結冰閱讀 169評論 0 1
  • 人生如夢姆蘸,一輩子很短墩莫,等真正看破浮生的時候,一生就已經過去一半逞敷。但是這“一半”狂秦,你是真的讀懂了人生,它已經夠你下半...
    燒火一條柴閱讀 638評論 0 4
  • 春種荷蓮三四缸推捐, 葉葉相連不見花裂问。 蛙鳴不解閑心事, 無花送風難度夏牛柒。
    大豐風雨守望者閱讀 441評論 1 3
  • 無聊 好無聊 無聊至極 你真的就這么無聊嗎堪簿? 什么是無聊 就是周圍都在忙碌的時候,你明明有事干皮壁,卻閑著椭更。 你明明可...
    丁先森吃西瓜閱讀 116評論 0 0