亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術(shù)文章
文章詳情頁

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

瀏覽:5日期:2022-09-23 15:44:57

樣式效果

還是先來看效果:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

這是一個(gè)仿雷達(dá)掃描的效果,是之前在做地圖sdk接入時(shí)就想實(shí)現(xiàn)的效果,但之前由于趕著畢業(yè)設(shè)計(jì),就沒有親手去實(shí)現(xiàn),不過現(xiàn)在自己擼一個(gè)發(fā)現(xiàn)還是挺簡(jiǎn)單的。

這里主要分享一下我的做法。

目錄

主體輪廓的實(shí)現(xiàn)(雷達(dá)的結(jié)構(gòu))

動(dòng)畫的實(shí)現(xiàn)(雷達(dá)掃描的效果)

目標(biāo)點(diǎn)的加入(圖片/點(diǎn))

主體輪廓實(shí)現(xiàn)

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

不難分析得出,這個(gè)View主要由外部的一個(gè)圓,中間的錨點(diǎn)圓以及扇形旋轉(zhuǎn)區(qū)域組成。而且每個(gè)部分理應(yīng)由不同的Paint去繪制,以方便去定制各部分的樣式。

外部圓以及錨點(diǎn)圓的繪制較為簡(jiǎn)單,主要的點(diǎn)還是要對(duì)整個(gè)View的寬高進(jìn)行一定的限制,例如寬高必須相等且在某種模式下,取小的那個(gè)值來限定整個(gè)RadarView的最大值。那么該如何去控制呢?

onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)

由于我們繼承自View,在onMeasure方法中,我們可以根據(jù)兩個(gè)參數(shù)來獲取Mode,并且根據(jù)Mode來指定寬/高對(duì)應(yīng)的值,再通過setMeasuredDimension去指定控件主體的寬高即可。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val vWidth = measureDimension(widthMeasureSpec) val vHeight = measureDimension(heightMeasureSpec) val size = min(vWidth, vHeight) setMeasuredDimension(size, size)} private fun measureDimension(spec: Int) = when (MeasureSpec.getMode(spec)) { MeasureSpec.EXACTLY -> { // exactly number or match_parent MeasureSpec.getSize(spec) } MeasureSpec.AT_MOST -> { // wrap_content min(mDefaultSize, MeasureSpec.getSize(spec)) } else -> { mDefaultSize }}

測(cè)量工作完成了,我們自然可以去繪制了。為了不讓中間的小圓看起來那么突兀(偏大或偏小),這里設(shè)置了一個(gè)scaleFactor的縮放因子,使其能根據(jù)外圓的尺寸來進(jìn)行縮放。

override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) // draw outside circle (background) canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, measuredWidth.toFloat() / 2, mOutlinePaint) if (mBorderWidth > 0F && mOutlinePaint.shader == null) { drawBorder(canvas) } // mOutlineRect = Rect(0, 0, measuredWidth, measuredHeight) canvas?.drawArc(mOutlineRect.toRectF(), mStartAngle, mSweepAngle, true, mSweepPaint) // draw center circle // scaleFactor = 30F canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, measuredWidth.toFloat() / 2 / mScaleFactor, mPaint)} private fun drawBorder(canvas: Canvas?) { Log.i('RadarView', 'drawBorder') mOutlinePaint.style = Paint.Style.STROKE mOutlinePaint.color = mBorderColor mOutlinePaint.strokeWidth = mBorderWidth canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, (measuredWidth.toFloat() - mBorderWidth) / 2, mOutlinePaint) // 還原 mOutlinePaint.style = Paint.Style.FILL_AND_STROKE mOutlinePaint.color = mBackgroundColor}

繪制了基準(zhǔn)圓以后,要實(shí)現(xiàn)雷達(dá)掃描時(shí)那種漸變的效果,我們可以通過SweepGradient來操作。通過指定中心點(diǎn),漸變顏色,以及顏色的分布,來定制掃描漸變的樣式,默認(rèn)的即時(shí)開頭時(shí)gif展示的那種。由于這里是從第一象限開始旋轉(zhuǎn),因此將旋轉(zhuǎn)的起點(diǎn)通過matrix逆時(shí)針旋轉(zhuǎn)90度,從而達(dá)到由淺入深的效果。

private fun setShader(size: Int) { val shader = SweepGradient(size.toFloat() / 2, size.toFloat() / 2, mScanColors?: mDefaultScanColors, // 可通過setScanColors()來定制顏色 floatArrayOf(0F, 0.5F, 1F)) // 這里默認(rèn)走平均分布 val matrix = Matrix() // 逆時(shí)針旋轉(zhuǎn)90度 matrix.setRotate(-90F, size.toFloat() / 2, size.toFloat() / 2) shader.setLocalMatrix(matrix) mSweepPaint.shader = shader}

這里完成了測(cè)量與繪制的工作,那么我們?cè)诓季掷镆靡院螅蜁?huì)看到這樣的效果:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

這時(shí),由于我們之前在測(cè)量的時(shí)候,將寬高最小值作為繪制的基準(zhǔn)大小給予了RadarView,因此measuredWidth和measuredHeight是相等的,但是由于在布局中指定了match_parent屬性,那么實(shí)際的控件寬高還是和父布局一致(在這里即占滿屏幕寬高,由于寬比高小,所以看到繪制的圖形會(huì)偏向上方;如果設(shè)置了高比寬小,那么繪制的圖形就會(huì)位于左側(cè))。一般的雷達(dá)控件應(yīng)該都是居中顯示的,所以我在這里也重寫了onLayout方法,來實(shí)現(xiàn)居中的效果。

override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { // 設(shè)置默認(rèn)居中 var l = left var r = right var t = top var b = bottom when { width > height -> { // 寬度比高度大 那么要設(shè)置默認(rèn)居中就得把left往右移 right往左移 l = (width - measuredWidth) / 2 r = width - l layout(l, t, r, b) } height > width -> { // 高度比寬度大 那么要設(shè)置默認(rèn)居中就得把top往下移 bottom往上移 t = (height - measuredHeight) / 2 b = height - t layout(l, t, r, b) } else -> super.onLayout(changed, left, top, right, bottom) }}

動(dòng)畫的實(shí)現(xiàn)

完成了繪制,接下來就是思考該如何讓他動(dòng)起來了。由繪制的代碼不難想到,我這里考慮的是通過mStartAngle的變化來控制繪制的角度旋轉(zhuǎn),而ValueAnimator則正好能獲取到每次更新時(shí)value的值,因此這里我選用了這個(gè)方案。

fun start() { Log.i('RadarView', 'animation start') mIsAnimating = true mAnimator.duration = 2000 mAnimator.repeatCount = ValueAnimator.INFINITE mAnimator.addUpdateListener { val angle = it.animatedValue as Float mStartAngle = angle // Log.i('RadarView', 'mStartAngle = $mStartAngle and curValue = ${it.animatedValue}') postInvalidate() } mAnimator.start()}

這里就需要注意一個(gè)點(diǎn),就是canvas在繪制時(shí),后繪制的會(huì)覆蓋在前繪制的圖像上,所以需要注意繪制的順序。當(dāng)然,這里也可以把mOutlineRect的寬高設(shè)置為measuredWidth - mBorderWidth,那么就能保證繪制填充角度時(shí),不會(huì)把邊界覆蓋。

至此,動(dòng)畫的效果便完成了。

目標(biāo)點(diǎn)的加入

首先,前兩點(diǎn)已經(jīng)能滿足大多的雷達(dá)掃描需求了。這里這個(gè)添加目標(biāo)點(diǎn)(target)純粹是我自己想加入的功能,因?yàn)橛X得可以結(jié)合地圖sdk的MapView來共同使用,目前也只是開發(fā)階段,擴(kuò)展性可能考慮得還不是特別充足,也還沒應(yīng)用到具體項(xiàng)目中。但是,總覺得自己想的功能也該試著去實(shí)踐一下~

這里主要運(yùn)用的圓的計(jì)算公式:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

由于Android的坐標(biāo)系的原點(diǎn)是在左上角,y軸過頂點(diǎn)向下延伸。由我們的繪制可知,此繪制圖像在坐標(biāo)系中的位置大概如下圖所示:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

那么,對(duì)應(yīng)的公式就為:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

要注意的是,這里r的計(jì)算會(huì)根據(jù)圖/點(diǎn)的設(shè)置來動(dòng)態(tài)計(jì)算,具體例子通過代碼來進(jìn)行分析。

// 隨機(jī)落點(diǎn)fun addTarget(size: Int, type: TYPE = TYPE.RANDOM) { val list = ArrayList<PointF>() val r = measuredWidth.toFloat() / 2 val innerRect = Rect((r - r / mScaleFactor).toInt(), (r - r / mScaleFactor).toInt(), (r + r / mScaleFactor).toInt(), (r + r / mScaleFactor).toInt()) // 圓的中心點(diǎn) val circle = PointF(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2) while (list.size < size) { val ranX = Random.nextDouble(0.0, r * 2.0).toFloat() val ranY = Random.nextDouble(0.0, r * 2.0).toFloat() val ranPointF = PointF(ranX, ranY) if (innerRect.contains(ranPointF.toPoint())) { continue } // 圓公式 if (!mNeedBitmap && (ranX - circle.x).pow(2) + (ranY - circle.y).pow(2) < (r - mTargetRadius - mBorderWidth).toDouble().pow(2.0)) { // 普通點(diǎn) addTargetFromType(type, list, ranX, ranY, r, ranPointF) } else if (mNeedBitmap && (ranX - circle.x).pow(2) + (ranY - circle.y).pow(2) < (r - mBorderWidth - max(mBitmap.width, mBitmap.height) / 2).toDouble().pow(2)) { // 圖 addTargetFromType(type, list, ranX, ranY, r, ranPointF) } else { continue } } mTargetList = list for (target in list) { Log.i('RadarView', 'target = [${target.x}, ${target.y}]') } invalidate()}

可以看到,當(dāng)target為普通點(diǎn)時(shí),r的計(jì)算還要減去targetRadius,即目標(biāo)點(diǎn)的半徑,同時(shí)還要減去邊界的寬度,如圖所示:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

當(dāng)target為圖時(shí),由于寬高不定,故除了邊界外,還要減去大的邊,那么r的計(jì)算則為:

Android實(shí)現(xiàn)雷達(dá)View效果的示例代碼

同時(shí)為了避免圖片的尺寸過大,這里同樣采取了一個(gè)默認(rèn)值與一個(gè)縮放因子,從而保證圖的完整性以及避免過大而引起的視覺丑化。

關(guān)于落點(diǎn)的位置,目前采取的是隨機(jī)落點(diǎn),如果應(yīng)用到地圖掃點(diǎn)的話,可以通過地圖sdk內(nèi)的距離計(jì)算工具再與RadarView的坐標(biāo)做一個(gè)比例轉(zhuǎn)換,從而達(dá)到雷達(dá)內(nèi)顯示該點(diǎn)具體方位。

關(guān)于落點(diǎn)的分布,目前提供了5種類型:分別是全象限隨機(jī)、第一象限、第二象限、第三象限與第四象限隨機(jī)。

Github

若須直接調(diào)用,可移步至 https://github.com/CarsonWoo/RadarView

完整代碼

class RadarView : View { enum class TYPE { RANDOM, FIRST, SECOND, THIRD, FOURTH } private val mPaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) } private val mSweepPaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) } private val mOutlinePaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) } private val mTargetPaint by lazy { Paint(Paint.ANTI_ALIAS_FLAG) } private val mDefaultSize = 120// px // limit the size of bitmap private var mBitmapMaxSize = 0F private var mBitmapWHRatio = 0F private val mScaleFactor = 30F private var mStartAngle = 0F private val mSweepAngle = -60F private var mScanColors: IntArray? = null private val mDefaultScanColors = intArrayOf(Color.parseColor('#0F7F7F7F'), Color.parseColor('#7F7F7F7F'), Color.parseColor('#857F7F7F')) private val mDefaultBackgroundColor = Color.WHITE private var mBackgroundColor: Int = mDefaultBackgroundColor private var mBorderColor: Int = Color.BLACK private var mBorderWidth = 0F private var mTargetColor: Int = Color.RED private var mTargetRadius = 10F private lateinit var mOutlineRect: Rect private val mAnimator = ValueAnimator.ofFloat(0F, 360F) private var mTargetList: ArrayList<PointF>? = null private var mIsAnimating = false private var mNeedBitmap = false private var mBitmap = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher) constructor(context: Context): this(context, null) constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet) init { mPaint.color = Color.GRAY mPaint.strokeWidth = 10F mPaint.style = Paint.Style.FILL_AND_STROKE mPaint.strokeJoin = Paint.Join.ROUND mPaint.strokeCap = Paint.Cap.ROUND mSweepPaint.style = Paint.Style.FILL mOutlinePaint.style = Paint.Style.FILL_AND_STROKE mOutlinePaint.color = mBackgroundColor mTargetPaint.style = Paint.Style.FILL mTargetPaint.color = mTargetColor mTargetPaint.strokeWidth = 10F } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { super.onMeasure(widthMeasureSpec, heightMeasureSpec) val vWidth = measureDimension(widthMeasureSpec) val vHeight = measureDimension(heightMeasureSpec) val size = min(vWidth, vHeight) setShader(size) setMeasuredDimension(size, size) setParamUpdate() } override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { // 設(shè)置默認(rèn)居中 var l = left var r = right var t = top var b = bottom when { width > height -> { // 寬度比高度大 那么要設(shè)置默認(rèn)居中就得把left往右移 right往左移 l = (width - measuredWidth) / 2 r = width - l layout(l, t, r, b) } height > width -> { // 高度比寬度大 那么要設(shè)置默認(rèn)居中就得把top往下移 bottom往上移 t = (height - measuredHeight) / 2 b = height - t layout(l, t, r, b) } else -> super.onLayout(changed, left, top, right, bottom) } } private fun setShader(size: Int) { val shader = SweepGradient(size.toFloat() / 2, size.toFloat() / 2, mScanColors?: mDefaultScanColors, floatArrayOf(0F, 0.5F, 1F)) val matrix = Matrix() matrix.setRotate(-90F, size.toFloat() / 2, size.toFloat() / 2) shader.setLocalMatrix(matrix) mSweepPaint.shader = shader } fun setScanColors(colors: IntArray) { this.mScanColors = colors setShader(measuredWidth) invalidate() } fun setRadarColor(@ColorInt color: Int) { this.mBackgroundColor = color this.mOutlinePaint.color = color invalidate() } fun setRadarColor(colorString: String) { if (!colorString.startsWith('#') || colorString.length != 7 || colorString.length != 9) { Log.e('RadarView', 'colorString parse error, please check your enter param') return } val color = Color.parseColor(colorString) setRadarColor(color) } fun setBorderColor(@ColorInt color: Int) { this.mBorderColor = color invalidate() } fun setBorderColor(colorString: String) { if (!colorString.startsWith('#') || colorString.length != 7 || colorString.length != 9) { Log.e('RadarView', 'colorString parse error, please check your enter param') return } val color = Color.parseColor(colorString) setBorderColor(color) } fun setRadarGradientColor(colors: IntArray) { val shader = SweepGradient(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, colors, null) mOutlinePaint.shader = shader invalidate() } fun setBorderWidth(width: Float) { this.mBorderWidth = width invalidate() } private fun setParamUpdate() { mOutlineRect = Rect(0, 0, measuredWidth, measuredHeight) mBitmapMaxSize = measuredWidth.toFloat() / mScaleFactor } private fun measureDimension(spec: Int) = when (MeasureSpec.getMode(spec)) { MeasureSpec.EXACTLY -> { // exactly number or match_parent MeasureSpec.getSize(spec) } MeasureSpec.AT_MOST -> { // wrap_content min(mDefaultSize, MeasureSpec.getSize(spec)) } else -> { mDefaultSize } } override fun setBackground(background: Drawable?) { // 取消傳統(tǒng)背景設(shè)置// super.setBackground(background) } override fun onDraw(canvas: Canvas?) { super.onDraw(canvas) // draw outside circle (background) canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, measuredWidth.toFloat() / 2, mOutlinePaint) if (mBorderWidth > 0F && mOutlinePaint.shader == null) { drawBorder(canvas) } canvas?.drawArc(mOutlineRect.toRectF(), mStartAngle, mSweepAngle, true, mSweepPaint) if (!mTargetList.isNullOrEmpty() && !mIsAnimating) { drawTarget(canvas) } // draw center circle canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, measuredWidth.toFloat() / 2 / mScaleFactor, mPaint) } private fun drawBorder(canvas: Canvas?) { Log.i('RadarView', 'drawBorder') mOutlinePaint.style = Paint.Style.STROKE mOutlinePaint.color = mBorderColor mOutlinePaint.strokeWidth = mBorderWidth canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2, (measuredWidth.toFloat() - mBorderWidth) / 2, mOutlinePaint) // 還原 mOutlinePaint.style = Paint.Style.FILL_AND_STROKE mOutlinePaint.color = mBackgroundColor } private fun drawTarget(canvas: Canvas?) { mTargetList?.let { Log.e('RadarView', 'draw target') for (target in it) { if (mNeedBitmap) { canvas?.drawBitmap(mBitmap, target.x - mBitmap.width / 2, target.y - mBitmap.height / 2, mTargetPaint) } else { canvas?.drawCircle(target.x, target.y, mTargetRadius, mTargetPaint) } } } } fun setBitmapEnabled(enabled: Boolean, drawable: Drawable) { // 這里是為了防止界面還未獲取到寬高時(shí) 會(huì)導(dǎo)致onMeasure走不到 那么maxSize就會(huì)為0 post { this.mNeedBitmap = enabled this.mBitmapWHRatio = drawable.intrinsicWidth.toFloat() / drawable.intrinsicHeight.toFloat() mBitmap = if (mBitmapWHRatio >= 1) { // 寬比高大 drawable.toBitmap( width = min(mBitmapMaxSize, drawable.intrinsicWidth.toFloat()).toInt(), height = (min(mBitmapMaxSize, drawable.intrinsicWidth.toFloat()) / mBitmapWHRatio).toInt(), config = Bitmap.Config.ARGB_8888) } else { // 高比寬大 drawable.toBitmap( height = min(mBitmapMaxSize, drawable.intrinsicHeight.toFloat()).toInt(), width = (min(mBitmapMaxSize, drawable.intrinsicHeight.toFloat()) * mBitmapWHRatio).toInt(), config = Bitmap.Config.ARGB_8888 ) } } } // 隨機(jī)落點(diǎn) fun addTarget(size: Int, type: TYPE = TYPE.RANDOM) { val list = ArrayList<PointF>() val r = measuredWidth.toFloat() / 2 val innerRect = Rect((r - r / mScaleFactor).toInt(), (r - r / mScaleFactor).toInt(), (r + r / mScaleFactor).toInt(), (r + r / mScaleFactor).toInt()) // 圓的中心點(diǎn) val circle = PointF(measuredWidth.toFloat() / 2, measuredHeight.toFloat() / 2) while (list.size < size) { val ranX = Random.nextDouble(0.0, r * 2.0).toFloat() val ranY = Random.nextDouble(0.0, r * 2.0).toFloat() val ranPointF = PointF(ranX, ranY) if (innerRect.contains(ranPointF.toPoint())) { continue } // 圓公式 if (!mNeedBitmap && (ranX - circle.x).pow(2) + (ranY - circle.y).pow(2) < (r - mTargetRadius - mBorderWidth).toDouble().pow(2.0)) { // 在圓內(nèi) addTargetFromType(type, list, ranX, ranY, r, ranPointF) } else if (mNeedBitmap && (ranX - circle.x).pow(2) + (ranY - circle.y).pow(2) < (r - mBorderWidth - max(mBitmap.width, mBitmap.height) / 2).toDouble().pow(2)) { addTargetFromType(type, list, ranX, ranY, r, ranPointF) } else { continue } } mTargetList = list for (target in list) { Log.i('RadarView', 'target = [${target.x}, ${target.y}]') } invalidate() } private fun addTargetFromType(type: TYPE, list: ArrayList<PointF>, ranX: Float, ranY: Float, r: Float, ranPointF: PointF) { when (type) { TYPE.RANDOM -> { list.add(ranPointF) } TYPE.FOURTH -> { if (ranX in r.toDouble()..2 * r.toDouble() && ranY in r.toDouble()..2 * r.toDouble()) { list.add(ranPointF) } } TYPE.THIRD -> { if (ranX in 0.0..r.toDouble() && ranY in r.toDouble()..2 * r.toDouble()) { list.add(ranPointF) } } TYPE.SECOND -> { if (ranX in 0.0..r.toDouble() && ranY in 0.0..r.toDouble()) { list.add(ranPointF) } } TYPE.FIRST -> { if (ranX in r.toDouble()..2 * r.toDouble() && ranY in 0.0..r.toDouble()) { list.add(ranPointF) } } } } fun start() { Log.i('RadarView', 'animation start') mIsAnimating = true mAnimator.duration = 2000 mAnimator.repeatCount = ValueAnimator.INFINITE mAnimator.addUpdateListener { val angle = it.animatedValue as Float mStartAngle = angle Log.i('RadarView', 'mStartAngle = $mStartAngle and curValue = ${it.animatedValue}') postInvalidate() } mAnimator.start() } fun start(startVal: Float, endVal: Float) { mIsAnimating = true mAnimator.setFloatValues(startVal, endVal) mAnimator.duration = 2000 mAnimator.repeatCount = ValueAnimator.INFINITE mAnimator.addUpdateListener { mStartAngle = it.animatedValue as Float Log.i('RadarView', 'mStartAngle = $mStartAngle and curValue = ${it.animatedValue}') postInvalidate() } mAnimator.start() } fun stop() { mIsAnimating = false if (mAnimator.isRunning) { mAnimator.cancel() mAnimator.removeAllListeners() } mStartAngle = 0F } }

調(diào)用方式

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) radar_view.setBorderWidth(5F) radar_view.setRadarColor(Color.TRANSPARENT) radar_view.setBitmapEnabled(true, resources.getDrawable(R.mipmap.ic_launcher_round))// radar_view.setScanColors(intArrayOf(Color.RED, Color.LTGRAY, Color.CYAN))// radar_view.setRadarGradientColor(intArrayOf(Color.RED, Color.GREEN, Color.BLUE)) btn_start.setOnClickListener { radar_view.start()// workThreadAndCallback() } btn_stop.setOnClickListener { radar_view.stop() radar_view.addTarget(7) }}

總結(jié)

到此這篇關(guān)于Android實(shí)現(xiàn)雷達(dá)View效果的文章就介紹到這了,更多相關(guān)android 雷達(dá)View效果內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!

標(biāo)簽: Android
相關(guān)文章:
主站蜘蛛池模板: 国产伦精品一区二区三区女 | 欧美中文字幕在线视频 | 国产资源在线视频 | 无码中文字幕日韩专区 | 国产手机精品自拍视频 | 久久久社区 | 免费视频爰爱太爽了 | 亚洲精品国产一区二区 | 91精品国产91久久久久久青草 | 亚洲成本人网亚洲视频大全 | 性激烈的欧美暴力三级视频 | 美女毛片在线看 | 国产剧情精品在线观看 | 国产精品午夜寂寞视频 | 91精品视频免费在线观看 | 伊人毛片 | 成人一区二区免费中文字幕 | 久久精品美女久久 | 久青草视频在线 | 大尺度做爰床戏呻吟免费观看91 | 成人另类视频 | 成人影院午夜久久影院 | 欧美一级aⅴ毛片 | 综合久久婷婷 | 久久综合网久久综合 | 黄色香蕉视频 | 欧美中文字幕在线视频 | 激情网站网址 | 一级美女黄色片 | 又黄又爽又猛午夜性色播在线播放 | 亚洲福利一区二区三区 | 美女被啪到深处喷水gif动态图视频 | 美国美女黄色片 | 久久亚洲私人国产精品 | 婷婷久久综合九色综合九七 | 亚洲欧美成人一区二区在线电影 | 在线视频欧美日韩 | 91视频青青草 | 久久久国产成人精品 | 一级美女片 | 久久日韩精品中文字幕网 |