欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于Android實現(xiàn)寫字板功能的代碼詳解

 更新時間:2025年04月29日 10:29:09   作者:Katie。  
在很多應用場景中,我們需要讓用戶進行自由繪畫或手寫輸入,比如電子合同、快遞簽收需要簽字確認,地圖/圖片標記、文檔批注等,所以本文給大家介紹了如何基于Android實現(xiàn)一個高度可定制的寫字板,需要的朋友可以參考下

一、項目介紹

1. 背景與應用場景

在很多應用場景中,我們需要讓用戶進行自由繪畫或手寫輸入,如:

  • 簽字確認:電子合同、快遞簽收

  • 繪圖涂鴉:社交 App 分享手繪內(nèi)容

  • 涂抹擦除:兒童教育繪畫

  • 標注批注:地圖/圖片標記、文檔批注

本項目將實現(xiàn)一個高度可定制的寫字板,滿足:

  • 自由繪制:支持多筆觸、多顏色、多粗細

  • 撤銷重做:可撤銷/重做操作

  • 清屏保存:一鍵清空、一鍵保存為圖片

  • 手勢優(yōu)化:平滑曲線、壓感模擬(粗細模擬)

  • UI 可定制:顏色面板、筆寬控制、清空/撤銷/保存按鈕

  • 組件化:封裝 DrawingBoardView,易于在任意布局中使用

2. 功能列表

  1. 繪制路徑:用戶觸摸屏幕實時繪制連續(xù)曲線

  2. 多顏色切換:提供調(diào)色板,支持任意顏色

  3. 可調(diào)筆寬:支持至少 3 種筆觸粗細

  4. 撤銷/重做:可對每一條路徑進行撤銷和重做

  5. 清空畫布:一鍵清空所有繪制內(nèi)容

  6. 保存圖片:將畫布內(nèi)容保存到本地相冊或應用私有目錄

  7. 導出分享:可直接分享繪制的圖片

  8. 性能優(yōu)化:支持硬件加速、路徑緩存、局部刷新

二、相關(guān)知識

在動手之前,你需要了解以下核心技術(shù)點:

  1. 自定義 View 與 Canvas

    • 重寫 onDraw(Canvas),使用 Canvas.drawPath(Path, Paint) 繪制路徑

    • 在 onTouchEvent(MotionEvent) 中根據(jù) ACTION_DOWN/MOVE/UP 構(gòu)建 Path

  2. 數(shù)據(jù)結(jié)構(gòu)與撤銷/重做

    • 使用 List<Path> 保存已完成路徑,用 Stack<Path> 保存被撤銷的路徑以支持重做

    • 每次完成一筆后將 currentPath 加入 paths,清空 redoStack

  3. 性能優(yōu)化

    • 緩存 Path 和 Paint 對象,避免頻繁分配

    • 在 invalidate(Rect) 中局部刷新觸摸區(qū)域,減少全屏重繪

  4. 觸摸平滑

    • 使用二次貝塞爾曲線平滑軌跡:path.quadTo(prevX, prevY, (x+prevX)/2, (y+prevY)/2)

  5. 文件保存與分享

    • 將 Bitmap 導出:在 DrawingBoardView 中生成 Bitmap 并 Canvas 一次性繪制底圖與所有路徑

    • 使用 MediaStore(Android Q+)或 FileOutputStream 保存到相冊

    • 使用 FileProvider 和 Intent.ACTION_SEND 分享圖片

  6. UI 組件

    • 使用 RecyclerView 或 LinearLayout 構(gòu)建顏色面板與筆寬面板

    • 使用 MaterialButtonFloatingActionButton 等承載撤銷、重做、清除、保存操作

三、實現(xiàn)思路

  1. 封裝 DrawingBoardView

    • 公共屬性:setStrokeColor(int)setStrokeWidth(float)undo()redo()clear()exportBitmap()

    • 事件處理:onTouchEvent 采集并平滑記錄觸摸軌跡;

  2. 主界面布局

    • 頂部按鈕區(qū)域:撤銷、重做、清空、保存

    • 中部 DrawingBoardView 占滿屏幕

    • 底部工具欄:顏色選擇、筆寬滑動條

  3. 文件存儲與分享

    • 在 MainActivity 中調(diào)用 drawingBoard.exportBitmap() 獲取 Bitmap,再保存或分享

    • 使用協(xié)程或后臺線程處理 I/O,顯示進度提示

  4. 狀態(tài)保存與恢復

    • 在 onSaveInstanceState 保存 paths 和 redoStack 的序列化數(shù)據(jù)

    • 在 onRestoreInstanceState 恢復路徑,避免屏幕旋轉(zhuǎn)丟失畫圖

  5. 模塊化與復用

    • 將所有繪制邏輯封裝在 DrawingBoardView.kt

    • 將保存與分享功能封裝在 ImageUtil.kt

四、環(huán)境與依賴

// app/build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
 
android {
  compileSdkVersion 34
  defaultConfig {
    applicationId "com.example.drawingboard"
    minSdkVersion 21
    targetSdkVersion 34
  }
  buildFeatures { viewBinding true }
  kotlinOptions { jvmTarget = "1.8" }
}
 
dependencies {
  implementation 'androidx.appcompat:appcompat:1.6.1'
  implementation 'androidx.core:core-ktx:1.10.1'
  implementation 'com.google.android.material:material:1.9.0'
  implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
}

五、整合代碼

// =======================================================
// 文件: res/layout/activity_main.xml
// 描述: 主界面布局,包含工具欄、DrawingBoardView、顏色/筆寬工具
// =======================================================
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent" android:layout_height="match_parent">
 
  <!-- 頂部操作欄 -->
  <com.google.android.material.appbar.MaterialToolbar
      android:id="@+id/toolbar"
      android:layout_width="match_parent"
      android:layout_height="?attr/actionBarSize"
      android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
      app:title="寫字板"/>
 
  <!-- 繪制面板 -->
  <com.example.drawingboard.DrawingBoardView
      android:id="@+id/drawingBoard"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:layout_marginTop="?attr/actionBarSize"
      android:background="#FFFFFF"/>
 
  <!-- 底部工具欄 -->
  <LinearLayout
      android:id="@+id/bottomTools"
      android:orientation="horizontal"
      android:gravity="center_vertical"
      android:padding="8dp"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_gravity="bottom"
      android:background="#CCFFFFFF">
 
    <!-- 顏色面板 -->
    <HorizontalScrollView
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="wrap_content">
      <LinearLayout
          android:id="@+id/colorPalette"
          android:orientation="horizontal"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"/>
    </HorizontalScrollView>
 
    <!-- 筆寬滑動條 -->
    <SeekBar
        android:id="@+id/seekStroke"
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:max="50"
        android:progress="10"
        android:layout_marginStart="16dp"/>
  </LinearLayout>
 
  <!-- 懸浮操作按鈕 -->
  <com.google.android.material.floatingactionbutton.FloatingActionButton
      android:id="@+id/btnClear"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      android:src="@drawable/ic_baseline_clear_24"
      app:layout_anchorGravity="bottom|end"
      app:layout_anchor="@id/drawingBoard"/>
 
  <com.google.android.material.floatingactionbutton.FloatingActionButton
      android:id="@+id/btnUndo"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      android:src="@drawable/ic_baseline_undo_24"
      app:layout_anchorGravity="bottom|start"
      app:layout_anchor="@id/drawingBoard"/>
 
  <com.google.android.material.floatingactionbutton.FloatingActionButton
      android:id="@+id/btnRedo"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      android:src="@drawable/ic_baseline_redo_24"
      app:layout_anchorGravity="bottom|start"
      app:layout_anchor="@id/btnUndo"/>
 
  <com.google.android.material.floatingactionbutton.FloatingActionButton
      android:id="@+id/btnSave"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      android:src="@drawable/ic_baseline_save_24"
      app:layout_anchorGravity="bottom|end"
      app:layout_anchor="@id/btnClear"/>
 
</androidx.coordinatorlayout.widget.CoordinatorLayout>
 
// =======================================================
// 文件: DrawingBoardView.kt
// 描述: 自定義繪制板,支持繪制、撤銷、重做、清空、導出
// =======================================================
package com.example.drawingboard
 
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import java.util.*
 
class DrawingBoardView @JvmOverloads constructor(
  context: Context, attrs: AttributeSet? = null
) : View(context, attrs) {
 
  // 畫筆與路徑集合
  private var paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
    color = Color.BLACK; strokeWidth = 10f
    style = Paint.Style.STROKE; strokeCap = Paint.Cap.ROUND
    strokeJoin = Paint.Join.ROUND
  }
  private var currentPath = Path()
  private val paths = mutableListOf<Pair<Path, Paint>>()
  private val redoStack = Stack<Pair<Path, Paint>>()
 
  // 觸摸上一個點
  private var prevX = 0f; private var prevY = 0f
 
  /** 設(shè)置畫筆顏色 */
  fun setStrokeColor(color: Int) { paint.color = color }
 
  /** 設(shè)置畫筆粗細 */
  fun setStrokeWidth(width: Float) { paint.strokeWidth = width }
 
  /** 撤銷 */
  fun undo() {
    if (paths.isNotEmpty()) redoStack.push(paths.removeAt(paths.lastIndex))
    invalidate()
  }
 
  /** 重做 */
  fun redo() {
    if (redoStack.isNotEmpty()) paths += redoStack.pop()
    invalidate()
  }
 
  /** 清空 */
  fun clear() {
    paths.clear(); redoStack.clear()
    invalidate()
  }
 
  /** 導出 Bitmap */
  fun exportBitmap(): Bitmap {
    val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(bmp)
    canvas.drawColor(Color.WHITE)
    for ((p, paint) in paths) canvas.drawPath(p, paint)
    return bmp
  }
 
  override fun onTouchEvent(e: MotionEvent): Boolean {
    val x = e.x; val y = e.y
    when (e.action) {
      MotionEvent.ACTION_DOWN -> {
        currentPath = Path().apply { moveTo(x, y) }
        prevX = x; prevY = y
        // 新操作清空 redo 棧
        redoStack.clear()
      }
      MotionEvent.ACTION_MOVE -> {
        val mx = (x + prevX) / 2
        val my = (y + prevY) / 2
        currentPath.quadTo(prevX, prevY, mx, my)
        prevX = x; prevY = y
      }
      MotionEvent.ACTION_UP -> {
        // 完成一筆,將路徑及其畫筆屬性存儲
        val p = Path(currentPath)
        val paintCopy = Paint(paint)
        paths += Pair(p, paintCopy)
      }
    }
    invalidate()
    return true
  }
 
  override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // 依次繪制歷史路徑
    for ((p, paint) in paths) canvas.drawPath(p, paint)
    // 繪制當前路徑
    canvas.drawPath(currentPath, paint)
  }
}
 
// =======================================================
// 文件: ImageUtil.kt
// 描述: 圖片保存與分享工具
// =======================================================
package com.example.drawingboard
 
import android.content.Context
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import java.io.*
 
object ImageUtil {
  /** 保存到相冊并返回 Uri */
  fun saveBitmapToGallery(ctx: Context, bmp: Bitmap, name: String = "draw_${System.currentTimeMillis()}"): Uri? {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
      val values = ContentValues().apply {
        put(MediaStore.Images.Media.DISPLAY_NAME, "$name.png")
        put(MediaStore.Images.Media.MIME_TYPE, "image/png")
        put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/DrawingBoard")
        put(MediaStore.Images.Media.IS_PENDING, 1)
      }
      val uri = ctx.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
      uri?.let {
        ctx.contentResolver.openOutputStream(it)?.use { os -> bmp.compress(Bitmap.CompressFormat.PNG, 100, os) }
        values.clear(); values.put(MediaStore.Images.Media.IS_PENDING, 0)
        ctx.contentResolver.update(it, values, null, null)
      }
      uri
    } else {
      val dir = File(ctx.getExternalFilesDir(null), "DrawingBoard")
      if (!dir.exists()) dir.mkdirs()
      val file = File(dir, "$name.png")
      FileOutputStream(file).use { fos -> bmp.compress(Bitmap.CompressFormat.PNG, 100, fos) }
      Uri.fromFile(file)
    }
  }
}
 
// =======================================================
// 文件: MainActivity.kt
// 描述: 主界面邏輯:初始化畫板、工具綁定、保存與分享
// =======================================================
package com.example.drawingboard
 
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.widget.ImageButton
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import com.example.drawingboard.databinding.ActivityMainBinding
import kotlinx.coroutines.*
 
class MainActivity : AppCompatActivity() {
  private lateinit var binding: ActivityMainBinding
  private val scope = CoroutineScope(Dispatchers.Main + Job())
 
  // 分享后臨時 Uri
  private var savedImageUri: Uri? = null
 
  // 分享授權(quán)
  private val shareLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
  ) { /* nothing */ }
 
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
 
    // 初始化顏色面板
    initColorPalette()
 
    // 筆寬控制
    binding.seekStroke.setOnSeekBarChangeListener(object: SimpleSeekListener(){
      override fun onProgressChanged(sb: androidx.appcompat.widget.AppCompatSeekBar, p: Int, u: Boolean) {
        binding.drawingBoard.setStrokeWidth(p.toFloat())
      }
    })
 
    // 頂部按鈕綁定
    binding.btnClear.setOnClickListener { binding.drawingBoard.clear() }
    binding.btnUndo.setOnClickListener  { binding.drawingBoard.undo() }
    binding.btnRedo.setOnClickListener  { binding.drawingBoard.redo() }
    binding.btnSave.setOnClickListener  { saveDrawing() }
  }
 
  private fun initColorPalette() {
    val colors = listOf(Color.BLACK, Color.RED, Color.BLUE, Color.GREEN, Color.MAGENTA)
    for (c in colors) {
      val btn = ImageButton(this).apply {
        val size = resources.getDimensionPixelSize(R.dimen.color_btn_size)
        layoutParams = androidx.appcompat.widget.LinearLayoutCompat.LayoutParams(size, size).apply {
          marginEnd = 16
        }
        setBackgroundColor(c)
        setOnClickListener { binding.drawingBoard.setStrokeColor(c) }
      }
      binding.colorPalette.addView(btn)
    }
  }
 
  private fun saveDrawing() {
    // 異步保存并分享
    scope.launch {
      val bmp = withContext(Dispatchers.Default) { binding.drawingBoard.exportBitmap() }
      savedImageUri = ImageUtil.saveBitmapToGallery(this@MainActivity, bmp)
      if (savedImageUri != null) {
        shareImage(savedImageUri!!)
      } else {
        Toast.makeText(this@MainActivity, "保存失敗", Toast.LENGTH_SHORT).show()
      }
    }
  }
 
  private fun shareImage(uri: Uri) {
    val contentUri = if (uri.scheme == "file") {
      FileProvider.getUriForFile(this, "$packageName.fileprovider", Uri.parse(uri.path!!).toFile())
    } else uri
    val intent = Intent(Intent.ACTION_SEND).apply {
      type = "image/png"
      putExtra(Intent.EXTRA_STREAM, contentUri)
      addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    }
    shareLauncher.launch(Intent.createChooser(intent, "分享繪圖"))
  }
 
  override fun onDestroy() {
    super.onDestroy()
    scope.cancel()
  }
}
 
// =======================================================
// 文件: SimpleSeekListener.kt
// 描述: 簡易 SeekBar 監(jiān)聽,省略回調(diào)實現(xiàn)
// =======================================================
package com.example.drawingboard
 
import android.widget.SeekBar
 
abstract class SimpleSeekListener: SeekBar.OnSeekBarChangeListener {
  override fun onStartTrackingTouch(p0: SeekBar?) {}
  override fun onStopTrackingTouch(p0: SeekBar?) {}
}

六、代碼解讀

  1. DrawingBoardView

    • 數(shù)據(jù)結(jié)構(gòu)paths: List<Pair<Path,Paint>> 保存每筆軌跡與對應畫筆;

    • 觸摸處理:使用 quadTo 平滑繪制;在 ACTION_UP 時深拷貝路徑與畫筆入 paths

    • 撤銷/重做undo() 從 paths 移出最后一筆入 redoStack;redo() 則反向操作;

    • 清空與導出clear() 清空所有,exportBitmap() 生成白底 Bitmap 并重繪所有路徑。

  2. ImageUtil

    • 兼容 Android Q+ 與以下版本,分別使用 MediaStore 或文件流保存;

    • 保存在 Pictures/DrawingBoard 或 getExternalFilesDir,并返回 Uri 便于分享。

  3. MainActivity

    • UI 綁定colorPalette 動態(tài)生成顏色按鈕,seekStroke 動態(tài)控制筆寬;

    • 操作按鈕:清空、撤銷、重做按鈕直接調(diào)用相應 API;

    • 保存與分享:協(xié)程異步導出 Bitmap→保存→拿到 Uri→通過 Intent.ACTION_SEND 分享;

  4. 權(quán)限與 URI

    • 使用 FileProvider 適配 Android 7.0+ 文件訪問限制;

    • 在 AndroidManifest.xml 與 provider_paths.xml 中正確配置;

七、性能與優(yōu)化

  1. 局部刷新

    • 可在 onTouchEvent 中記錄變化區(qū)域,用 invalidate(left, top, right, bottom) 替代全局刷新;

  2. 對象復用

    • 避免在每次觸摸時創(chuàng)建新 Paint 或 Path 對象,可維護池化策略;

  3. 內(nèi)存管理

    • 對于大畫布或長時間繪制,注意 Bitmap 內(nèi)存,必要時使用 inBitmap 重用;

  4. 多點觸控

    • 擴展至支持多指同時繪制,每根手指一條 Path;

八、項目總結(jié)與拓展

  • 本文完整實現(xiàn)了一個功能完備的寫字板組件,涵蓋自由繪制、撤銷重做、清空、保存與分享的全流程。

  • 通過組件化封裝,業(yè)務層僅需在布局中引用 DrawingBoardView 并綁定按鈕,即可快速集成。

拓展方向

  1. 筆壓感應:結(jié)合手寫筆壓力,動態(tài)調(diào)整筆寬或透明度;

  2. 圖形標注:支持直線、矩形、圓形、文字等多種標注模式;

  3. 云端同步:將繪制數(shù)據(jù)以矢量格式上傳服務器,實現(xiàn)跨端同步;

  4. 動畫回放:記錄繪制時間戳,支持繪制過程回放;

  5. Jetpack Compose 重構(gòu):使用 Canvas 與 Modifier.pointerInput 實現(xiàn) Compose 版寫字板。

九、FAQ

  1. Q:如何保存多頁畫布?
    A:可在 paths 加入頁面索引,導出時分別按照頁碼生成多張 Bitmap 并打包。

  2. Q:Bitmap 導出后圖片太大怎么辦?
    A:在保存時對 Bitmap 進行壓縮,或先縮放至合適尺寸。

  3. Q:如何讓撤銷支持部分筆跡?
    A:目前按整筆撤銷,若需精細撤銷可將每段 quadTo 拆分為更小路徑并記錄。

  4. Q:如何在旋轉(zhuǎn)屏幕后保持繪制?
    A:在 onSaveInstanceState 序列化 paths 數(shù)據(jù),旋轉(zhuǎn)后在 onRestoreInstanceState 中恢復。

  5. Q:如何支持涂鴉橡皮擦功能?
    A:可在涂鴉模式下切換 paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) 來擦除軌跡。

以上就是基于Android實現(xiàn)寫字板功能的代碼詳解的詳細內(nèi)容,更多關(guān)于Android寫字板功能的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Android 運用@JvmName解決函數(shù)簽名沖突問題詳解

    Android 運用@JvmName解決函數(shù)簽名沖突問題詳解

    JvmName注解是Kotlin提供的一個可以變更編譯器輸出的注解,這里簡單的介紹一下其使用規(guī)則,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-07-07
  • android h5頁面獲取不到定位數(shù)據(jù)的問題解決

    android h5頁面獲取不到定位數(shù)據(jù)的問題解決

    我們經(jīng)常會遇到onGeolocationPermissionsShowPrompt 已經(jīng)執(zhí)行,但仍然沒有獲取到定位數(shù)據(jù)的問題,所以本文給大家介紹了android h5頁面獲取不到定位數(shù)據(jù)的問題解決,需要的朋友可以參考下
    2024-11-11
  • android列表控件實現(xiàn)展開、收縮功能

    android列表控件實現(xiàn)展開、收縮功能

    這篇文章主要為大家詳細介紹了android支持展開/收縮功能的列表控件,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Android ListView實現(xiàn)仿iPhone實現(xiàn)左滑刪除按鈕的簡單實例

    Android ListView實現(xiàn)仿iPhone實現(xiàn)左滑刪除按鈕的簡單實例

    下面小編就為大家?guī)硪黄狝ndroid ListView實現(xiàn)仿iPhone實現(xiàn)左滑刪除按鈕的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • Android Fragment 基本了解(圖文介紹)

    Android Fragment 基本了解(圖文介紹)

    Android是在Android 3.0 (API level 11)開始引入Fragment的可以把Fragment想成Activity中的模塊,這個模塊有自己的布局,有自己的生命周期,單獨處理自己的輸入,在Activity運行的時候可以加載或者移除Fragment模塊
    2013-01-01
  • Android實現(xiàn)圓形圖片的兩種方式

    Android實現(xiàn)圓形圖片的兩種方式

    這篇文章主要為大家詳細介紹了Android實現(xiàn)圓形圖片的兩種方式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-11-11
  • Android 限制顯示小數(shù)點后兩位的實現(xiàn)方法

    Android 限制顯示小數(shù)點后兩位的實現(xiàn)方法

    下面小編就為大家分享一篇Android 限制顯示小數(shù)點后兩位的實現(xiàn)方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-01-01
  • 淺談Android 照相機權(quán)限的聲明

    淺談Android 照相機權(quán)限的聲明

    今天小編就為大家分享一篇淺談Android 照相機權(quán)限的聲明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-08-08
  • Android webview手動校驗https證書(by 星空武哥)

    Android webview手動校驗https證書(by 星空武哥)

    有些時候由于Android系統(tǒng)的bug或者其他的原因,導致我們的webview不能驗證通過我們的https證書,最明顯的例子就是華為手機mate7升級到Android7.0后,手機有些網(wǎng)站打不開了,而更新了webview的補丁后就沒問題了
    2017-09-09
  • Android 判斷網(wǎng)絡狀態(tài)及開啟網(wǎng)路

    Android 判斷網(wǎng)絡狀態(tài)及開啟網(wǎng)路

    這篇文章主要介紹了Android 判斷網(wǎng)絡狀態(tài)及開啟網(wǎng)路的相關(guān)資料,在開發(fā)網(wǎng)路狀態(tài)的時候需要先判斷是否開啟之后在提示用戶進行開啟操作,需要的朋友可以參考下
    2017-08-08

最新評論