国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

android SectorMenuView底部導航扇形菜單的實現代碼

2019-10-22 18:15:37
字體:
來源:轉載
供稿:網友

這次分析一個扇形菜單展開的自定義View, 也是我實習期間做的一個印象比較深刻的自定義View, 前后切換了很多種實現思路, 先看看效果展示

效果展示

android,底部導航,SectorMenuView,扇形菜單

效果分析

  1. 點擊圓形的FloatActionBar, 自身旋轉一定的角度
  2. 菜單像波紋一樣擴散開來
  3. 顯示我們添加的item

實現分析

使用adapter適配器去設置View, 用戶可自定義性強, 不過每次使用需要去設置Adapter, 較為繁瑣

直接調用ItemView, 將ImageView和TextView寫死, 用戶操作簡單, 但是缺乏可定制性(利他)

本次功能實現采用了方案 2

實現步驟

  1. 與氣泡拖拽類似, 新開啟一個Window進行自定義View的繪制
  2. 初始化時調用setWillNotDraw(false)方法, 強行啟動ViewGroup的繪制
  3. onMeasure中將寬高寫死
  4. 繪制背景
    1. 錨點為View的底部中心點
    2. 半徑為屏幕寬度一半的平方和的開方(注意這里不是屏幕的一半)
  5. 添加itemView, 在onLayout中去確定其位置
  6. 添加動畫效果
  7. 將相關接口暴露給外界

使用方式

BottomSectorMenuView.Converter(mFab)        .setToggleDuration(500, 800)        .setAnchorRotationAngle(135f)        .addMenuItem(R.drawable.icon_camera, "拍照") { Toast.makeText(this@MainActivity, "拍照", Toast.LENGTH_SHORT).show() }        .addMenuItem(R.drawable.icon_photo, "圖片") { Toast.makeText(this@MainActivity, "圖片", Toast.LENGTH_SHORT).show() }        .addMenuItem(R.drawable.icon_text, "文字") { Toast.makeText(this@MainActivity, "文字", Toast.LENGTH_SHORT).show() }        .addMenuItem(R.drawable.icon_video, "視頻") { Toast.makeText(this@MainActivity, "視頻", Toast.LENGTH_SHORT).show() }        .addMenuItem(R.drawable.icon_camera_shooting, "攝像") { Toast.makeText(this@MainActivity, "攝像", Toast.LENGTH_SHORT).show() }        .apply()

源碼實現

/** * Email: frankchoochina@gmail.com * Created by FrankChoo on 2017/10/9. * Description: 底部扇形菜單, 通過Adapter添加Item *       1. 調用openMenu打開菜單 *       2. 調用closeMenu關閉菜單 */public class SectorMenuView extends FrameLayout {  // 每個ItemView之間的角度差  private double mAngle;  // 圓心坐標  private Point mCenterPoint;  // ItemView到圓心的半徑  private float mMaxItemRadius;  private float mCurItemRadius;  // 背景圓的半徑  private float mMaxBkgRadius;  private float mCurBkgRadius;  private Paint mPaint;  private SectorMenuAdapter mAdapter;  private OnMenuOpenedListener mMenuOpenedListener;  private OnMenuClosedListener mMenuClosedListener;  public SectorMenuView(Context context) {    this(context, null);  }  public SectorMenuView(Context context, AttributeSet attrs) {    this(context, attrs, 0);  }  public SectorMenuView(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    init();  }  private void init() {    // 初始化畫筆    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    mPaint.setDither(true);    mPaint.setStyle(Paint.Style.FILL);    mPaint.setColor(Color.WHITE);    // 設置背景圓繪制的半徑    int displayWidth = getResources().getDisplayMetrics().widthPixels;    mMaxBkgRadius = (int) Math.sqrt(Math.pow(displayWidth/2, 2.0) + Math.pow(displayWidth/2, 2.0));    // 開啟ViewGroup的繪制    setWillNotDraw(false);  }  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    // 這里直接將寬高寫死, 不支持Margin    int width = getResources().getDisplayMetrics().widthPixels;    int height = (int) Math.sqrt(Math.pow(width / 2, 2.0) + Math.pow(width / 2, 2.0));    setMeasuredDimension(width, height);    // 計算半徑    int realWidth = width - getPaddingRight() - getPaddingLeft();    int realHeight = height - getPaddingTop() - getPaddingBottom();    mMaxItemRadius = realWidth / 2;    // 計算圓心    int centerX = getPaddingLeft() + realWidth / 2;    int centerY = getPaddingTop() + realHeight;    mCenterPoint = new Point(centerX, centerY);  }  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {    super.onLayout(changed, l, t, r, b);    for (int i = 0; i < getChildCount(); i++) {      View child = getChildAt(i);      double curAngle = Math.PI - mAngle * (i + 1);      int childCenterX = (int) (mCenterPoint.x + mCurItemRadius * Math.cos(curAngle));      int childCenterY = (int) (mCenterPoint.y - mCurItemRadius * Math.sin(curAngle));      child.layout(          childCenterX - child.getMeasuredWidth() / 2,          childCenterY - child.getMeasuredHeight() / 2,          childCenterX + child.getMeasuredWidth() / 2,          childCenterY + child.getMeasuredHeight() / 2      );      // 這里動態的去設置子View的透明度      child.setAlpha(mCurItemRadius / mMaxItemRadius);    }  }  @Override  protected void onDraw(Canvas canvas) {    canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mCurBkgRadius, mPaint);    super.onDraw(canvas);  }  public void setAdapter(SectorMenuAdapter adapter) {    mAdapter = adapter;    for (int i = 0; i < mAdapter.getCount(); i++) {      View child = mAdapter.getView(i, null, this);      addView(child);    }    mAngle = Math.PI / (mAdapter.getCount() + 1);  }  public void setBackgroudColor(@ColorInt int color) {    mPaint.setColor(color);  }  public void setBackgroundResource(@ColorRes int colorResId) {    mPaint.setColor(ContextCompat.getColor(getContext(), colorResId));  }  /**   * 打開菜單   */  public void openMenu() {    if (mMaxItemRadius == 0) {      mMaxItemRadius = getResources().getDisplayMetrics().widthPixels / 2          - getPaddingRight() - getPaddingLeft();    }    // 背景動畫    ValueAnimator bkgAnim = ValueAnimator.ofFloat(0f, mMaxBkgRadius).setDuration(300);    bkgAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        mCurBkgRadius = (float) animation.getAnimatedValue();        invalidate();      }    });    // item的位置動畫    ValueAnimator itemTranslationAnim = ValueAnimator.ofFloat(0f, mMaxItemRadius).setDuration(300);    itemTranslationAnim.setInterpolator(new OvershootInterpolator(2f));    itemTranslationAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        mCurItemRadius = (float) animation.getAnimatedValue();        requestLayout();      }    });    // 動畫集合    final AnimatorSet set = new AnimatorSet();    set.playSequentially(bkgAnim, itemTranslationAnim);    set.addListener(new AnimatorListenerAdapter() {      @Override      public void onAnimationStart(Animator animation) {        setAlpha(1f);        setVisibility(View.VISIBLE);      }      @Override      public void onAnimationEnd(Animator animation) {        if (mMenuOpenedListener != null) {          mMenuOpenedListener.opened();        }      }    });    set.start();  }  /**   * 關閉菜單   */  public void closeMenu() {    // Item動畫    ValueAnimator itemViewAnim = ValueAnimator.ofFloat(mMaxItemRadius, 0f).setDuration(300);    itemViewAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        mCurItemRadius = (float) animation.getAnimatedValue();        requestLayout();      }    });    itemViewAnim.setInterpolator(new AnticipateInterpolator(2f));    // 背景動畫    ValueAnimator backgroundAnim = ValueAnimator.ofFloat(mMaxBkgRadius, 0f).setDuration(300);    backgroundAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        mCurBkgRadius = (float) animation.getAnimatedValue();        invalidate();      }    });    // 這里設置了該View整體透明度的變化, 防止消失的背景不在錨點處, 顯示效果突兀    ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, "alpha", 1f, 0f).setDuration(250);    // 動畫集合    AnimatorSet animatorSet = new AnimatorSet();    animatorSet.play(itemViewAnim).before(backgroundAnim);    animatorSet.play(backgroundAnim).with(alphaAnim);    animatorSet.addListener(new AnimatorListenerAdapter() {      @Override      public void onAnimationEnd(Animator animation) {        if (mMenuClosedListener != null) {          mMenuClosedListener.closed();        }        setVisibility(View.INVISIBLE);      }    });    animatorSet.start();  }  public void setOnMenuOpenedListener(OnMenuOpenedListener listener) {    mMenuOpenedListener = listener;  }  public void setOnMenuClosedListener(OnMenuClosedListener listener) {    mMenuClosedListener = listener;  }  /**   * 供外界調用的Adapter   */  public abstract static class SectorMenuAdapter extends BaseAdapter {    @Override    public long getItemId(int position) {      return 0;    }    @Override    public Object getItem(int position) {      return null;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {      return createView(position, parent);    }    protected abstract View createView(int position, ViewGroup parent);    @Override    public abstract int getCount();  }  public interface OnMenuOpenedListener {    void opened();  }  public interface OnMenuClosedListener {    void closed();  }}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 钟山县| 车致| 五河县| 惠安县| 沁阳市| 河间市| 清水河县| 临泽县| 盖州市| 阳曲县| 合肥市| 宁海县| 商丘市| 梅河口市| 涪陵区| 卢氏县| 深水埗区| 枣阳市| 安远县| 阜平县| 姚安县| 沭阳县| 正阳县| 易门县| 中西区| 收藏| 余江县| 托克托县| 景德镇市| 陆丰市| 徐汇区| 中阳县| 伊吾县| 荆门市| 天祝| 陵川县| 龙川县| 醴陵市| 乳山市| 阆中市| 独山县|