前言:
通過(guò)Camera, Matrix 3d旋轉(zhuǎn)+RecyclerView實(shí)現(xiàn)和(IOS時(shí)間地址選擇3D)滾輪控件一樣效果的WheelView繼承ViewGroup,實(shí)現(xiàn)安卓QQ上滾輪一樣的滑動(dòng)效果
更多文章請(qǐng)關(guān)注:http://www.reibang.com/u/b1cff340957c
一:先看效果圖
使用方式
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
dependencies {
implementation 'com.github.youxiaochen:WheelView-3d:1.4.1'
}
布局生成WheelView方式 有默認(rèn)屬性
<chen.you.wheel.WheelView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:wheelOrientation="vertical"
app:wheelItemCount="3"
app:wheelItemSize="30dp"
app:wheelGravity="center"
app:wheelTextSize="18sp"
app:wheelTextColor="#333333"
app:wheelTextCenterColor="#ff00ff"
app:wheelDividerSize="1dp"
app:wheelDividerColor="#00ff00"
app:wheelGradient="true"
app:wheelDividerPadding="2dp"/>
wv.setAdapter(new WheelView.Adapter() {
@Override
protected String getItem(int position) {
return "position " + position;
}
@Override
protected int getItemCount() {
return 100;
}
});
代碼生成WheelView及擴(kuò)展方式
//需要擴(kuò)展功能時(shí)
WheelParams params = new WheelParams.Builder()
.setOrientation(WheelParams.HORIZONTAL)
.setItemSize(...)
.setTextColor(...)
WheelView wv = new WheelView(context, params);
...
//代碼設(shè)置各種屬性
//亦可用此方式設(shè)置各屬性
wv.getWheelParams().newBuilder().setOrientation(...)
wv.setWheelParams(params);
//設(shè)置繪制管理, 默認(rèn)為WheelDrawManager產(chǎn)生3D旋轉(zhuǎn), 亦可設(shè)置LinearDrawManager不旋轉(zhuǎn), 也可自定義DrawManager擴(kuò)展
wv.setDrawManager(new WheelDrawManager());
//設(shè)置繪制器, 默認(rèn)為SimpleItemPainter, 也可自定義繪制器擴(kuò)展
wv.setItemPainter(...)
二:功能分析 3D旋轉(zhuǎn)效果
WheelView的實(shí)現(xiàn)方式已經(jīng)有很多種方式, 而且網(wǎng)上也有實(shí)現(xiàn)好的旋轉(zhuǎn)效果,不過(guò)只是2D的旋轉(zhuǎn),而且要處理滑動(dòng)與單擊item事件比較復(fù)雜,真正的旋轉(zhuǎn)是要通過(guò)Matrix, Camera類來(lái)實(shí)現(xiàn),這里的Camera不是照相機(jī)里的API,Camera可以實(shí)現(xiàn)x,y,z軸的旋轉(zhuǎn),不清楚的可以去也解這些API的使用, 這里不詳細(xì)介紹, 配合RecyclerView.ItemDecoration,在每個(gè)item中將Canvas進(jìn)行3D旋轉(zhuǎn)并平移,產(chǎn)生3D視覺效果
這里拿垂直布局的一種狀態(tài)來(lái)做示例
/**
* 畫垂直布局時(shí)的item
* @param c
* @param rect
* @param position
* @param parentCenterX RecyclerView的中心X點(diǎn)
* @param parentCenterY RecyclerView的中心Y點(diǎn)
*/
void drawVerticalItem(Canvas c, Rect rect, int position, float parentCenterX, float parentCenterY) {
int realPosition = position - itemCount;//數(shù)據(jù)中的實(shí)際位置
float itemCenterY = rect.exactCenterY();
float scrollOffY = itemCenterY - parentCenterY;
float rotateDegreeX = scrollOffY * itemDegree / itemSize;//垂直布局時(shí)要以X軸為中心旋轉(zhuǎn)
int alpha = degreeAlpha(rotateDegreeX);
if (alpha <= 0) return;
float rotateSinX = (float) Math.sin(Math.toRadians(rotateDegreeX));
float rotateOffY = scrollOffY - wheelRadio * rotateSinX;//因旋轉(zhuǎn)導(dǎo)致界面視角的偏移
//Log.i("you", "drawVerticalItem degree " + rotateDegreeX);
//計(jì)算中心item, 優(yōu)先最靠近中心區(qū)域的為中心點(diǎn)
boolean isCenterItem = false;
if (!hasCenterItem) {
isCenterItem = Math.abs(scrollOffY) <= halfItemHeight;
if (isCenterItem) {
centerItemPosition = realPosition;
hasCenterItem = true;
}
}
//這里是旋轉(zhuǎn)操作的核心,每個(gè)item在旋轉(zhuǎn)成弧時(shí)寇漫,都要將item的中心在旋轉(zhuǎn)后給人的視覺上的偏移計(jì)算好
c.save();
c.translate(0.0f, -rotateOffY);//因旋轉(zhuǎn)導(dǎo)致界面視角的偏移
camera.save();
//旋轉(zhuǎn)時(shí)離視角的z軸方向也會(huì)變化,先移動(dòng)Z軸再旋轉(zhuǎn)
float z = (float) (wheelRadio * (1 - Math.abs(Math.cos(Math.toRadians(rotateDegreeX)))));
camera.translate(0, 0, z);
camera.rotateX(-rotateDegreeX);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-translateX, -itemCenterY);
matrix.postTranslate(translateX, itemCenterY);
c.concat(matrix);
drawItem(c, rect, realPosition, alpha, isCenterItem, true);
c.restore();
}
到這里基本已經(jīng)實(shí)現(xiàn)了每個(gè)item距離中心點(diǎn)的旋轉(zhuǎn)效果,接下來(lái)就是添加WheelView顯示的數(shù)量在RecyclerView頭與尾部的空的item
最后附上源碼 https://github.com/youxiaochen/WheelView-3d
總結(jié):
WheelView具體使用方法,示例代碼中都有詳細(xì)介紹