自定义侧边栏SideView-支持Android插值器

最近开发中有个需求,需要实现侧边栏Sidebar并带有特定的动效。

自定义侧边栏SideView:支持Android插值器
GitHub:https://github.com/lyldding/QuickSideView
CSDN:https://blog.csdn.net/lylddingHFFW/article/details/87880749

1:效果图:

2:使用

xml:

<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="test"
        android:textColor="@color/colorPrimary"
        android:textSize="26sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <com.lyldding.quicksideview.SideView
        android:id="@+id/right_side"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/test1"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="LinearInterpolator"
        android:layout_marginTop="20dp"/>

    <TextView
        android:id="@+id/test2"
        android:layout_marginTop="20dp"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="BounceInterpolator"
        app:layout_constraintTop_toBottomOf="@id/test1"/>

    <TextView
        android:id="@+id/test3"
        android:layout_marginTop="20dp"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="OvershootInterpolator"
        app:layout_constraintTop_toBottomOf="@id/test2"/>

</android.support.constraint.ConstraintLayout>

java:

public class MainActivity extends AppCompatActivity implements SideView.OnSelectedListener {
    private TextView mTextView;
    private SideView mSideView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = findViewById(R.id.text);
        mSideView = findViewById(R.id.right_side);
        mSideView.setOnSelectedListener(this);

        findViewById(R.id.test1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSideView.setInterpolator(new LinearInterpolator());
            }
        });

        findViewById(R.id.test2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSideView.setInterpolator(new BounceInterpolator());
                mSideView.setSelectColor(Color.BLUE);
            }
        });

        findViewById(R.id.test3).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSideView.setInterpolator(new OvershootInterpolator());
            }
        });
    }

    @Override
    public void onSelected(int currentPosition, String s) {
        mTextView.setText(s);
    }
}

3: 实现

public class SideView extends View {
    private static final String TAG = "SideLetterView";
    private List<String> mLetters;
    private List<Float> mCacheWidth;
    private Paint mPaint;
    private int mTextHeight;
    private int mInitWidth;
    private int mRadius = 0;
    private int mItemHeight;
    private float mCurrentY;
    private int mCurrentPosition = -1;
    private int mDefaultTextSize = 36;
    private float mDrawTextX;
    private boolean mIsTouching;
    private boolean mIsInit;
    private int mPaddingTop;
    private Interpolator mInterpolator;
    private int mSelectColor;
    private int mDefaultColor;


    private OnSelectedListener mOnSelectedListener;

    public SideView(Context context) {
        super(context);
        init(context);
    }

    public SideView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public SideView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }


    private void init(Context context) {
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        mPaint.setTextSize(mDefaultTextSize);

        mSelectColor = Color.RED;
        mDefaultColor = Color.BLACK;
        mInterpolator = new LinearInterpolator();
        initLettersInfo();
        mIsInit = true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = getSize(widthMeasureSpec, 100);
        int heightSize = getSize(heightMeasureSpec, mTextHeight * mLetters.size() + 50);

        if (mIsInit) {
            mInitWidth = widthSize;
            mIsInit = false;
        }
        mPaddingTop = getPaddingTop();
        mItemHeight = (heightSize - getPaddingBottom() - mPaddingTop) / mLetters.size();
        setMeasuredDimension(mRadius + mInitWidth, heightSize);
    }

    private int getSize(int measureSpec, int defaultSize) {
        int size = MeasureSpec.getSize(measureSpec);
        int mode = MeasureSpec.getMode(measureSpec);
        if (mode != MeasureSpec.EXACTLY) {
            size = defaultSize;
        }
        return size;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int index = 0; index < mLetters.size(); index++) {
            updateDrawText(index);
            canvas.drawText(mLetters.get(index),
                    mDrawTextX,
                    mPaddingTop + mItemHeight * (index + 1) - (mItemHeight - mTextHeight) * 0.5f,
                    mPaint);
        }
    }

    /**
     * 更新绘制文字的属性
     *
     * @param index
     */
    private void updateDrawText(int index) {
        int textSize = mDefaultTextSize;
        mDrawTextX = mRadius + (mInitWidth - mCacheWidth.get(index)) * 0.5f;
        if (mIsTouching) {
            float scale = Math.abs(mCurrentY - index * mItemHeight) * 1f / mRadius;
            if (scale <= 1) {
                mDrawTextX = (mInterpolator.getInterpolation(scale)) * mRadius;
                textSize = (int) (mDefaultTextSize * (2 - scale));
            }
        }
        mPaint.setTextSize(textSize);
        mPaint.setColor(mCurrentPosition == index ? mSelectColor : mDefaultColor);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        Log.d(TAG, "onTouchEvent: action = " + action);
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                mIsTouching = true;
                mCurrentY = event.getY();
                mRadius = 300;
                notifyCurrentPosition();
                requestLayout();
                break;
            case MotionEvent.ACTION_MOVE:
                mIsTouching = true;
                mCurrentY = event.getY();
                notifyCurrentPosition();
                ViewCompat.postInvalidateOnAnimation(this);
                break;
            default:
                mRadius = 0;
                mIsTouching = false;
                mCurrentPosition = -1;
                requestLayout();
        }

        return true;
    }

    /**
     * 更新选择的位置
     */
    private void notifyCurrentPosition() {
        int oldPosition = mCurrentPosition;
        mCurrentPosition = (int) (mCurrentY / mItemHeight);
        if (mCurrentPosition < 0) {
            mCurrentPosition = 0;
        } else if (mCurrentPosition >= mLetters.size()) {
            mCurrentPosition = mLetters.size() - 1;
        }
        if (mOnSelectedListener != null && oldPosition != mCurrentPosition) {
            mOnSelectedListener.onSelected(mCurrentPosition, mLetters.get(mCurrentPosition));
        }
    }

    interface OnSelectedListener {
        /**
         * 选择内容
         *
         * @param currentPosition 位置
         * @param text            内容
         */
        void onSelected(int currentPosition, String text);
    }

    public void setOnSelectedListener(OnSelectedListener onSelectedListener) {
        mOnSelectedListener = onSelectedListener;
    }

    /**
     * 设置快速选择内容
     *
     * @param letters
     */
    public void setLetters(List<String> letters) {
        mLetters.clear();
        mLetters.addAll(letters);
        if (mLetters.size() == 0) {
            throw new IllegalArgumentException("mLetters.size() == 0");
        }
        cacheLetterWidth();
        requestLayout();
    }

    /**
     * 初始化
     */
    private void initLettersInfo() {
        mLetters = new ArrayList<>();
        mCacheWidth = new ArrayList<>();
        for (char index = 'A'; index <= 'Z'; index++) {
            mLetters.add(String.valueOf(index));
        }
        cacheLetterWidth();
    }

    /**
     * 缓存每个类别的初始宽度
     */
    private void cacheLetterWidth() {
        mCacheWidth.clear();
        for (String letter : mLetters) {
            float width = mPaint.measureText(letter);
            mCacheWidth.add(width);
        }
        Rect rect = new Rect();
        mPaint.getTextBounds(mLetters.get(0), 0, mLetters.get(0).length(), rect);
        mTextHeight = rect.height();
    }

    /**
     * 设置插值器
     */
    public void setInterpolator(Interpolator interpolator) {
        mInterpolator = interpolator;
    }

    /**
     * 设置选中颜色
     *
     * @param selectColor
     */
    public void setSelectColor(int selectColor) {
        mSelectColor = selectColor;
    }

    /**
     * 设置默认颜色
     *
     * @param defaultColor
     */
    public void setDefaultColor(int defaultColor) {
        mDefaultColor = defaultColor;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Ionic侧边栏是一个非常实用的组件,可以让用户轻松地在不同的页面之间进行导航。ion-side-menus是Ionic中用于创建侧边栏的指令,它提供了多个选项来控制侧边栏的外观和功能。 要使用ion-side-menus指令,需要在HTML中创建一个包含ion-side-menus指令的容元素。然后,在容中定义一个ion-side-menu-content指令,用于指定主要内容区域。在主要内容区域中,可以使用ion-nav-view指令来显示不同的页面。 在侧边栏中,可以使用ion-side-menu指令来定义侧边栏的内容。可以在侧边栏中包含任意数量的元素,包括列表、按钮和链接等。 当用户在侧边栏中选择一个项目时,可以使用ion-tap指令来处理点击事件。ion-tap指令可以与ng-click指令一起使用,用于在视图中触发特定的函数或操作。 下面是一个示例代码,展示了如何使用ion-side-menus和ion-tap指令来创建一个具有侧边栏导航的Ionic应用: ``` <ion-side-menus> <!-- 左侧侧边栏 --> <ion-side-menu side="left"> <ion-header-bar class="bar-dark"> <h1 class="title">菜单</h1> </ion-header-bar> <ion-content> <ion-list> <ion-item ng-click="openPage('home')">首页</ion-item> <ion-item ng-click="openPage('about')">关于</ion-item> <ion-item ng-click="openPage('contact')">联系我们</ion-item> </ion-list> </ion-content> </ion-side-menu> <!-- 主要内容区域 --> <ion-side-menu-content> <ion-header-bar class="bar-dark"> <button class="button button-icon ion-navicon" ng-click="toggleLeft()"> </button> <h1 class="title">我的应用</h1> </ion-header-bar> <ion-nav-view></ion-nav-view> </ion-side-menu-content> </ion-side-menus> ``` 在上面的代码中,我们创建了一个包含ion-side-menus指令的容元素。在容中,我们定义了一个包含ion-side-menu指令的左侧侧边栏,并在其中添加了一些项目。在主要内容区域中,我们使用ion-nav-view指令来显示不同的页面,并使用ion-header-bar指令来添加导航栏。在导航栏中,我们添加了一个按钮,用于打开左侧侧边栏,并使用ion-tap指令来处理点击事件。 最后,我们还需要在控制中定义一个openPage函数,用于在点击侧边栏中的项目时导航到不同的页面。 ``` .controller('MyCtrl', function($scope, $ionicSideMenuDelegate, $state) { $scope.openPage = function(page) { $ionicSideMenuDelegate.toggleLeft(false); $state.go(page); }; $scope.toggleLeft = function() { $ionicSideMenuDelegate.toggleLeft(); }; }); ``` 在上面的代码中,我们定义了一个openPage函数,用于在点击侧边栏中的项目时导航到不同的页面。我们还定义了一个toggleLeft函数,用于打开或关闭左侧侧边栏。在openPage函数中,我们通过$state.go函数来导航到不同的页面,并在导航完成后关闭左侧侧边栏。在toggleLeft函数中,我们使用$ionicSideMenuDelegate.toggleLeft函数来打开或关闭左侧侧边栏。 总之,使用ion-side-menus和ion-tap指令可以轻松创建具有侧边栏导航的Ionic应用,并实现侧边栏中项目的点击事件处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值