最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android粒子線條效果實(shí)現(xiàn)過(guò)程與代碼

 更新時(shí)間:2023年02月09日 09:29:29   作者:撿一晌貪歡  
這篇文章主要介紹了Android粒子線條效果的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧

前言

很久沒(méi)寫代碼了,忙工作、忙朋友、人也懶了,最近重新調(diào)整自己,對(duì)技術(shù)還是要有熱情,要熱情的話還是用自定義view做游戲有趣,寫完這個(gè)粒子線條后面我會(huì)更新幾個(gè)小游戲博文及代碼,希望讀者喜歡。

這個(gè)粒子效果的控件是去年寫的,寫的很差勁,這幾天又重構(gòu)了一下,還是難看的要命,勉強(qiáng)記錄下吧。

需求

主要就是看到博客園的粒子線條背景很有意思,就想模仿一下。核心思想如下:

1、隨機(jī)出現(xiàn)點(diǎn)

2、范圍內(nèi)的點(diǎn)連線

3、手指按下,加入點(diǎn),范圍內(nèi)點(diǎn)向手指移動(dòng)

效果圖

效果圖就是難看,沒(méi)得說(shuō)。

代碼

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import java.lang.ref.WeakReference
import kotlin.math.pow
import kotlin.math.sqrt
/**
 * 模仿博客粒子線條的view
 *
 * 核心思想簡(jiǎn)易版
 *
 * 1、隨機(jī)出現(xiàn)點(diǎn)
 * 2、范圍內(nèi)的點(diǎn)連線
 * 3、手指按下,加入點(diǎn),范圍內(nèi)點(diǎn)向手指移動(dòng)
 *
 * @author silence
 * @date 2022-11-09
 *
 */
class ParticleLinesBgView @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
    defStyleAttr: Int = 0
): View(context, attributeSet, defStyleAttr){
    companion object{
        // 屏幕刷新時(shí)間,每秒20次
        const val SCREEN_FLUSH_TIME = 50L
        // 新增點(diǎn)的間隔時(shí)間
        const val POINT_ADD_TIME = 200L
        // 粒子存活時(shí)間
        const val POINT_ALIVE_TIME = 18000L
        // 吸引的合適距離
        const val ATTRACT_LENGTH = 250f
        // 維持的合適距離
        const val PROPER_LENGTH = 150f
        // 粒子被吸引每次接近的距離
        const val POINT_MOVE_LENGTH = 30f
        // 距離計(jì)算公式
        fun getDistance(x1: Float, y1: Float, x2: Float, y2: Float): Float {
            return sqrt(((x1 - x2).toDouble().pow(2.0)
                    + (y1 - y2).toDouble().pow(2.0)).toFloat())
        }
    }
    // 存放的粒子
    private val mParticles = ArrayList<Particle>(64)
    // 手指按下位置
    private var mTouchParticle: Particle? = null
    // 處理的handler
    private val mHandler = ParticleHandler(this)
    // 畫筆
    private val mPaint = Paint().apply {
        color = Color.LTGRAY
        strokeWidth = 3f
        style = Paint.Style.STROKE
        flags = Paint.ANTI_ALIAS_FLAG
    }
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 通過(guò)發(fā)送消息給handler實(shí)現(xiàn)間隔添加點(diǎn)
        mHandler.removeMessages(0)
        mHandler.sendEmptyMessageDelayed(0, POINT_ADD_TIME)
    }
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 繪制點(diǎn)和線
        for (i in 0 until mParticles.size) {
            val point = mParticles[i]
            canvas.drawPoint(point.x, point.y, mPaint)
            // 連線
            for (j in (i + 1) until mParticles.size) {
                val another = mParticles[j]
                val distance = getDistance(point.x, point.y, another.x, another.y)
                if (distance <= PROPER_LENGTH) {
                    canvas.drawLine(point.x, point.y, another.x, another.y, mPaint)
                }
            }
        }
        mTouchParticle?.let {
            // 手指按下點(diǎn)與附近連線
            for(point in mParticles) {
                val distance = getDistance(point.x, point.y, it.x, it.y)
                if (distance <= PROPER_LENGTH) {
                    canvas.drawLine(point.x, point.y, it.x, it.y, mPaint)
                }
            }
            // 吸引范圍顯示
            mPaint.color = Color.BLUE
            canvas.drawCircle(it.x, it.y, PROPER_LENGTH, mPaint)
            mPaint.color = Color.LTGRAY
        }
    }
    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        when(event.action) {
            MotionEvent.ACTION_DOWN -> {
                mTouchParticle = Particle(event.x, event.y, 0)
            }
            MotionEvent.ACTION_MOVE -> {
                mTouchParticle!!.x = event.x
                mTouchParticle!!.y = event.y
                invalidate()
            }
            MotionEvent.ACTION_UP -> {
                mTouchParticle = null
            }
        }
        return true
    }
    // 粒子
    class Particle(var x: Float, var y: Float, var counter: Int)
    // kotlin自動(dòng)編譯為Java靜態(tài)類,控件引用使用弱引用
    class ParticleHandler(view: ParticleLinesBgView): Handler(Looper.getMainLooper()){
        // 控件引用
        private val mRef: WeakReference<ParticleLinesBgView> = WeakReference(view)
        // 粒子出現(xiàn)控制
        private var mPointCounter = 0
        override fun handleMessage(msg: Message) {
            mRef.get()?.let {view->
                // 新增點(diǎn)
                mPointCounter++
                if (mPointCounter == (POINT_ADD_TIME / SCREEN_FLUSH_TIME).toInt()) {
                    // 隨機(jī)位置
                    val x = (Math.random() * view.width).toFloat()
                    val y = (Math.random() * view.height).toFloat()
                    view.mParticles.add(Particle(x, y, 0))
                    mPointCounter = 0
                }
                val iterator = view.mParticles.iterator()
                while (iterator.hasNext()) {
                    val point = iterator.next()
                    // 移除失活粒子
                    if (point.counter == (POINT_ALIVE_TIME / SCREEN_FLUSH_TIME).toInt()) {
                        iterator.remove()
                    }
                    // 手指按下時(shí),粒子朝合適的距離移動(dòng)
                    view.mTouchParticle?.let {
                        val distance = getDistance(point.x, point.y, it.x, it.y)
                        if(distance in PROPER_LENGTH..ATTRACT_LENGTH) {
                            // 橫向接近
                            if (point.x < it.x) point.x += POINT_MOVE_LENGTH
                            else point.x -= POINT_MOVE_LENGTH
                            // 縱向接近
                            if (point.y < it.y) point.y += POINT_MOVE_LENGTH
                            else point.y -= POINT_MOVE_LENGTH
                        }else if(distance <= PROPER_LENGTH) {
                            // 橫向遠(yuǎn)離
                            if (point.x < it.x) point.x -= POINT_MOVE_LENGTH
                            else point.x += POINT_MOVE_LENGTH
                            // 縱向遠(yuǎn)離
                            if (point.y < it.y) point.y -= POINT_MOVE_LENGTH
                            else point.y += POINT_MOVE_LENGTH
                        }
                    }
                }
                // 循環(huán)發(fā)送
                view.invalidate()
                view.mHandler.sendEmptyMessageDelayed(0, POINT_ADD_TIME)
            }
        }
    }
}

這里沒(méi)寫onMeasure,注意下不能用wrap-content,布局的話改個(gè)黑色背景就行了。

主要問(wèn)題

下面簡(jiǎn)單講講吧。

粒子

這里用了個(gè)數(shù)據(jù)類構(gòu)造了粒子,用了一個(gè)ArrayList來(lái)存放,本來(lái)想用linkedHashMap來(lái)保存并實(shí)現(xiàn)下LRU的,結(jié)果連線的時(shí)候比較復(fù)雜,重構(gòu)的時(shí)候直接刪了,后面用了一個(gè)counter來(lái)控制粒子的存活時(shí)間。

邏輯控制

一開始的時(shí)候想的比較復(fù)雜,實(shí)現(xiàn)來(lái)弄得自己頭疼,后面覺(jué)得何不將邏輯和繪制分離,在ondraw里面只進(jìn)行繪制不就行了,邏輯通過(guò)handler來(lái)更新,實(shí)際這樣在我看來(lái)是對(duì)的。

我這用了一個(gè)Handler配合嵌套循環(huán)發(fā)送空消息,實(shí)現(xiàn)定時(shí)更新效果,每隔一段時(shí)間更新一下邏輯,Handler內(nèi)部通過(guò)弱引用獲得view,并對(duì)其中的內(nèi)容修改,修改完成后,通過(guò)invalidate出發(fā)線程更新。

新增點(diǎn)

Handler會(huì)定時(shí)更新,只需要在handleMessage里面添加點(diǎn)就行了,為了控制點(diǎn)出現(xiàn)的頻率,我這又引入了控制變量。

粒子生命周期

handleMessage里面會(huì)檢查粒子是否失活,失活了就通過(guò)iterator去移除,移除數(shù)組內(nèi)內(nèi)容還是盡量通過(guò)iterator去實(shí)現(xiàn)吧,特別是for-eacn循環(huán)以及for循環(huán)內(nèi)刪除多個(gè)時(shí),會(huì)出錯(cuò)的!

粒子趨向于手指

手指按下時(shí)設(shè)置mTouchParticle,移動(dòng)時(shí)更新這個(gè)mTouchParticle,手指抬起時(shí)對(duì)mTouchParticle賦空,這樣在handleMessage里面只要在mTouchParticle不為空時(shí)稍稍改變下其他粒子的位置,就可以達(dá)到趨向效果。

粒子連線

這里我沒(méi)有想到什么好辦法,直接兩兩計(jì)算,并對(duì)合適距離的粒子進(jìn)行連線。

到此這篇關(guān)于Android粒子線條效果實(shí)現(xiàn)過(guò)程與代碼的文章就介紹到這了,更多相關(guān)Android粒子線條內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Android RecyclerView使用入門介紹

    Android RecyclerView使用入門介紹

    RecyclerView是Android一個(gè)更強(qiáng)大的控件,其不僅可以實(shí)現(xiàn)和ListView同樣的效果,還有優(yōu)化了ListView中的各種不足。其可以實(shí)現(xiàn)數(shù)據(jù)縱向滾動(dòng),也可以實(shí)現(xiàn)橫向滾動(dòng)(ListView做不到橫向滾動(dòng))。接下來(lái)講解RecyclerView的用法
    2022-10-10
  • Android如何實(shí)現(xiàn)藍(lán)牙配對(duì)連接功能

    Android如何實(shí)現(xiàn)藍(lán)牙配對(duì)連接功能

    Android 并沒(méi)有開放配對(duì)連接耳機(jī)的接口,而且網(wǎng)上大部分資料都是講解如何連接藍(lán)牙4.0的,很少有資料詳細(xì)介紹藍(lán)牙2.0相關(guān)的。期間還是踩了不少坑才摸索出解決辦法。所以把我自己摸索總結(jié)出來(lái)的經(jīng)驗(yàn)梳理記錄下,以便備份
    2021-05-05
  • Flutter 如何正確顯示SnackBar

    Flutter 如何正確顯示SnackBar

    Snackbar是Android支持庫(kù)中用于顯示簡(jiǎn)單消息并且提供和用戶的一個(gè)簡(jiǎn)單操作的一種彈出式提醒。當(dāng)使用Snackbar時(shí),提示會(huì)出現(xiàn)在消息最底部,通常含有一段信息和一個(gè)可點(diǎn)擊的按鈕。本文主要介紹了Flutter 如何正確顯示 SnackBar
    2021-05-05
  • Android自定義可編輯、刪除的側(cè)滑LisitView

    Android自定義可編輯、刪除的側(cè)滑LisitView

    這篇文章主要為大家詳細(xì)介紹了Android自定義可編輯、刪除的側(cè)滑LisitView,感興趣的小伙伴們可以參考一下
    2016-07-07
  • Android調(diào)用系統(tǒng)的發(fā)郵件功能的小例子

    Android調(diào)用系統(tǒng)的發(fā)郵件功能的小例子

    這篇文章介紹了Android調(diào)用系統(tǒng)的發(fā)郵件功能的小例子,有需要的朋友可以參考一下
    2013-08-08
  • Android注冊(cè)登錄實(shí)時(shí)自動(dòng)獲取短信驗(yàn)證碼

    Android注冊(cè)登錄實(shí)時(shí)自動(dòng)獲取短信驗(yàn)證碼

    注冊(cè)登錄或修改密碼功能常常需要輸入短信驗(yàn)證碼,如何自動(dòng)獲取短信驗(yàn)證碼,這篇文章就為大家介紹了Androidcv注冊(cè)登錄自動(dòng)獲取短信驗(yàn)證碼的實(shí)現(xiàn)代碼,感興趣的小伙伴們可以參考一下
    2016-08-08
  • Android中PopupMenu組件的使用實(shí)例

    Android中PopupMenu組件的使用實(shí)例

    本篇文章主要介紹了Android中PopupMenu組件的使用實(shí)例,詳細(xì)的介紹了PopupMenu組件的使用,具體一定的參考價(jià)值,有興趣的可以了解一下
    2017-07-07
  • Android編程四大組件之BroadcastReceiver(廣播接收者)用法實(shí)例

    Android編程四大組件之BroadcastReceiver(廣播接收者)用法實(shí)例

    這篇文章主要介紹了Android編程四大組件之BroadcastReceiver(廣播接收者)用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了BroadcastReceiver的功能.定義,用法及相關(guān)使用技巧,需要的朋友可以參考下
    2016-01-01
  • Android入門之TableLayout應(yīng)用解析(一)

    Android入門之TableLayout應(yīng)用解析(一)

    這篇文章主要介紹了Android入門之TableLayout應(yīng)用,需要的朋友可以參考下
    2014-08-08
  • 淺談Android studio 生成apk文件時(shí)的 key store path 的問(wèn)題

    淺談Android studio 生成apk文件時(shí)的 key store path 的問(wèn)題

    這篇文章主要介紹了淺談Android studio 生成apk文件時(shí)的 key store path 的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-03-03

最新評(píng)論

舟曲县| 潮安县| 吉木乃县| 特克斯县| 托克托县| 吉隆县| 盖州市| 德钦县| 苏尼特左旗| 东辽县| 南澳县| 全州县| 定结县| 红桥区| 柞水县| 台湾省| 祁连县| 开阳县| 安阳县| 定襄县| 沙雅县| 乌拉特前旗| 萨嘎县| 云霄县| 赞皇县| 孝昌县| 陈巴尔虎旗| 抚顺市| 龙川县| 松原市| 雷州市| 敖汉旗| 武功县| 南丰县| 卓资县| 尉犁县| 雅安市| 太保市| 云安县| 青浦区| 毕节市|