前言
作為一個(gè)懂點(diǎn)Android的搬磚猿,作為一個(gè)有孩子的爸爸,看到孩子天天玩農(nóng)藥,就想著怎么減少他玩農(nóng)藥的時(shí)間.后來(lái)觀察了他一段時(shí)間,總結(jié)出一個(gè)規(guī)律:他剛進(jìn)游戲的時(shí)候會(huì)先去用鉆石抽獎(jiǎng),看到轉(zhuǎn)來(lái)轉(zhuǎn)去的動(dòng)畫,他還特別高興.于是乎就用android的知識(shí),寫了一個(gè)九宮格抽獎(jiǎng)的Demo.
思考
怎么樣實(shí)現(xiàn)一個(gè)九宮格那?我想到的是Android現(xiàn)有的控件有哪些可以實(shí)現(xiàn):GridView,RecyclerView,這些都可以實(shí)現(xiàn)九宮格,且我都實(shí)現(xiàn)了一遍.但是寫出來(lái)之后,我覺(jué)得沒(méi)有什么挑戰(zhàn)性,就想著:作為一個(gè)Android搬轉(zhuǎn)猿,怎么也得用自定義View實(shí)現(xiàn)這樣的效果.于是,就去做了.
怎么樣自定義一個(gè)九宮偶格那?我在打草紙上畫了一個(gè)九宮格,它有一個(gè)圓角矩形作為背景,背景的上層是由九個(gè)小圓角矩形的方塊組成的,每一個(gè)方塊都有一個(gè)數(shù)字,中間的那個(gè)方塊比較特殊,它是一個(gè)圓,點(diǎn)擊它就會(huì)有一個(gè)顏色塊在它周圍順時(shí)針轉(zhuǎn)動(dòng),頗有眾星拱月的感覺(jué).
在寫代碼之前,先思考幾個(gè)問(wèn)題:
自定義View的步驟
由于我們需要手動(dòng)畫一個(gè)九宮格,所以我們需要繼承View,重寫onDraw().如何安排每一個(gè)小方塊的位置與大小
我們可以把view的寬高等分成9份,這樣我們就計(jì)算出了每一份的大小,再計(jì)算每一份的左上角的坐標(biāo),這樣就實(shí)現(xiàn)了9個(gè)方塊的布局.如何實(shí)現(xiàn)順時(shí)針的動(dòng)畫
觀察我們畫在紙上的九宮格,模擬著顏色塊走過(guò)的軌跡,可以得出一個(gè)軌跡對(duì)應(yīng)到每一個(gè)方塊索引的關(guān)系數(shù)組orderArray(0,1,2,5,8,7,6,3).得到一個(gè)軌跡數(shù)組后,就可以通過(guò)實(shí)現(xiàn)顏色塊的移動(dòng)了.不明白的可以看往下的代碼.你寫的代碼review了嗎
自我審查代碼時(shí)自己感覺(jué)不舒服的地方,就是我們代碼有待改進(jìn)的地方,也是我們提升自我編碼能力的途徑之一.所以,多做檢查,發(fā)現(xiàn)自己的不足,再?gòu)浹a(bǔ)自己的不足.
代碼
九宮格View
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import java.lang.IllegalArgumentExceptiondata class ItemInfo(val reward: Int)class NineSpinView : View {private val mTextPaint: Paint = Paint()private val mBgPaint: Paint = Paint()private val mHitPaint: Paint = Paint()private var mBackgroundColor = 0private var mTextSize = 0Fprivate var mTextColor = 0private var mRowAndColumn = 0private var mRadius = 0Fprivate var mCenterIndex = 0private var mHitColor = 0private var mItemImg: Drawable? = nullprivate var mCenterImg: Drawable? = nullprivate var mItemSize = 0fprivate var mBitmapCache: HashMap<Int, Bitmap?>? = nullprivate var centerRectF: RectF? = nullprivate var mCompleteListener: (() -> Unit)? = nullprivate var mCenterListener: (() -> Unit)? = nullprivate val orderArray = intArrayOf(0, 1, 2, 5, 8, 7, 6, 3)private var hitIndex = -1private var repeatCount = 0private var targetIndex = -1private var isSpinning = falseprivate var isFinish = falseprivate var dataList:ArrayList<ItemInfo>? = nullcompanion object {const val TAG = "NineSpinView"const val MAX_REPEAT_COUNT = 3const val DEFAULT_TEXT_SIZE = 31Fconst val DEFAULT_RADIUS = 0Fconst val DEFAULT_ROW_COLUMN = 3const val DEFAULT_TEXT_COLOR = Color.BLACKconst val DEFAULT_HIT_COLOR = Color.YELLOW}constructor(context: Context?) : super(context)constructor(context: Context?, attributes: AttributeSet?) : super(context, attributes) {val typeArray = context?.resources?.obtainAttributes(attributes, R.styleable.NineSpinView)if (typeArray != null) {mBackgroundColor =typeArray.getColor(R.styleable.NineSpinView_spin_backgroundColor, 0)mTextSize =typeArray.getDimension(R.styleable.NineSpinView_spin_textSize,DEFAULT_TEXT_SIZE)mTextColor =typeArray.getColor(R.styleable.NineSpinView_spin_textColor,DEFAULT_TEXT_COLOR)mHitColor =typeArray.getColor(R.styleable.NineSpinView_spin_hit_color,DEFAULT_HIT_COLOR)mRowAndColumn =typeArray.getInt(R.styleable.NineSpinView_spin_rowAndColumn,DEFAULT_ROW_COLUMN)mRadius = typeArray.getDimension(R.styleable.NineSpinView_spin_radius,DEFAULT_RADIUS)mItemImg = typeArray.getDrawable(R.styleable.NineSpinView_spin_item_img)mCenterImg = typeArray.getDrawable(R.styleable.NineSpinView_spin_center_img)typeArray.recycle()}}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)mItemSize = (width / mRowAndColumn).toFloat()mCenterIndex = mRowAndColumn * mRowAndColumn / 2if (mTextSize > mItemSize) {mTextSize /= 3} else if (mTextSize > mItemSize / 2) {mTextSize = mTextSize * 2 / 3}initBitmapCache()initPaint()}private fun initBitmapCache() {mBitmapCache = HashMap()val centerImg = BitmapFactory.decodeResource(resources, R.drawable.spin_nine_center)val itemImg = ImageUtils.drawable2Bitmap(mItemImg!!)val width = itemImg.widthval height = itemImg.heightval matrix = Matrix()mRadius *= (mItemSize * 0.98f / width.toFloat())matrix.setScale(mItemSize * 0.98f / width.toFloat(),mItemSize * 0.98f / height.toFloat())val scaleItemImg = Bitmap.createBitmap(itemImg, 0, 0, width, height, matrix, true)val scaleCenterImg =Bitmap.createBitmap(centerImg, 0, 0, centerImg.width, centerImg.height, matrix, true)(0 until mRowAndColumn * mRowAndColumn).forEach { index ->val newBitmap = if (index == mCenterIndex) {scaleCenterImg} else {scaleItemImg}mBitmapCache?.set(index, newBitmap)}}private fun initPaint() {mTextPaint.isAntiAlias = truemTextPaint.style = Paint.Style.FILLmTextPaint.color = mTextColormTextPaint.textSize = mTextSizemTextPaint.textAlign = Paint.Align.CENTERmTextPaint.isDither = truemBgPaint.isAntiAlias = truemBgPaint.color = mBackgroundColormBgPaint.style = Paint.Style.FILLmHitPaint.isAntiAlias = truemHitPaint.color = mHitColormHitPaint.style = Paint.Style.FILL}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)// 背景層drawBackground(canvas)// 內(nèi)容層drawContent(canvas)}private fun drawBackground(canvas: Canvas?) {canvas?.drawRoundRect(0f,0f,width.toFloat(),height.toFloat(),mRadius,mRadius,mBgPaint)}private fun drawContent(canvas: Canvas?) {val size = mItemSizevar row = 0(0 until mRowAndColumn * mRowAndColumn).forEach { i ->val column = i % mRowAndColumnif (column == 0 && i > 0) {row++}val left = column * size + 3val top = row * size + 3drawItem(left, top, canvas, i, i)}}override fun onTouchEvent(event: MotionEvent?): Boolean {if (event?.action == MotionEvent.ACTION_UP) {val touchX = event.xval touchY = event.ycenterRectF?.let { centerRectF ->if (touchX > centerRectF.left&& touchX < centerRectF.right&& touchY > centerRectF.top &&touchY < centerRectF.bottom) {if (isSpinning) {return@let}isSpinning = truereset()this.mCenterListener?.invoke()}}}return true}private fun drawItem(left: Float, top: Float, canvas: Canvas?, imgId: Int, index: Int) {val newBitmap: Bitmap? = mBitmapCache?.get(imgId)if (index == mCenterIndex) {val bitmap: Bitmap = newBitmap!!if (centerRectF == null) {centerRectF = RectF(left, top, left + mItemSize, top + mItemSize)}val imgLeft = left + mItemSize / 2 - (newBitmap.width.div(2))val imgTop = top + mItemSize / 2 - (newBitmap.height.div(2))canvas?.drawBitmap(bitmap, imgLeft, imgTop, null)} else {val bitmap: Bitmap = newBitmap!!canvas?.drawBitmap(bitmap, left, top, null)if (index == hitIndex) {canvas?.drawRoundRect(left,top,left + mItemSize,top + mItemSize,mRadius,mRadius,mHitPaint)}val nLeft = left + mItemSize / 2val nTop = top + mItemSize * 1.2f / 2if (dataList != null && dataList!!.size <= mRowAndColumn*mRowAndColumn) {val reward = dataList!![index].rewardcanvas?.drawText("x $reward", nLeft, nTop, mTextPaint)} else {canvas?.drawText("x $index", nLeft, nTop, mTextPaint)}}}fun startSpinWithTargetReward(reward: Int) {dataList?.forEachIndexed{index, itemInfo ->if (reward == itemInfo.reward) {this.targetIndex = indexreturn@forEachIndexed}}spinNineAnim()}private fun spinNineAnim() {orderArray.forEachIndexed { index, i ->handler.postDelayed({if (isFinish) {return@postDelayed}hitIndex = iif (targetIndex == i && repeatCount == MAX_REPEAT_COUNT) {isSpinning = falseisFinish = truethis.mCompleteListener?.invoke()return@postDelayed}invalidate()if (i == orderArray.last() && repeatCount++ < MAX_REPEAT_COUNT) {spinNineAnim()}}, ((index + 1) * 100).toLong())}}fun setData(data:ArrayList<ItemInfo>) {if (data.size <= 0 && data.size > mRowAndColumn * mRowAndColumn) throw IllegalArgumentException("data not use")this.dataList = data}fun setOnCenterClickListener(clickListener: () -> Unit) {this.mCenterListener = clickListener}fun setCompleteListener(complete: () -> Unit) {this.mCompleteListener = complete}private fun reset() {hitIndex = -1itargetIndex = -1repeatCount = 0isFinish = falseinvalidate()}
}
j簡(jiǎn)單使用
<NineSpinViewandroid:id="@+id/view_nie_spin"android:layout_width="match_parent"android:layout_height="0dp"android:layout_gravity="center"android:layout_margin="50dp"android:background="#00ffffff"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintDimensionRatio="1:1"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"app:spin_hit_color="#a0fff000"app:spin_item_img="@drawable/spin_nine_item_bg"app:spin_radius="13dp"app:spin_textColor="@color/purple_200"app:spin_textSize="30sp" />private fun initNineSpin() {val nineSpinView = findViewById<NineSpinView>(R.id.view_nie_spin)val dataList = ArrayList<ItemInfo>(9)(5..13).forEach {dataList.add(ItemInfo(it))}nineSpinView.setData(dataList)nineSpinView.setOnCenterClickListener {val targetIndex = (10..13).random()nineSpinView.startSpinWithTargetReward(targetIndex)}nineSpinView.setCompleteListener {Log.e("TAG", "spin complete:----- ", )}}
總結(jié)
心有猛虎,細(xì)嗅薔薇.現(xiàn)在細(xì)品這句話,覺(jué)的滿符合我們學(xué)習(xí)新知識(shí),應(yīng)用新知識(shí)的歷程.
在雨后初晴的清晨,猛虎靜坐,鼻尖微觸,這妖艷的薔薇.
總結(jié)
以上是生活随笔為你收集整理的Android - 九宫格的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。