日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

《移动项目实践》实验报告——Android自定义控件

發(fā)布時間:2024/10/5 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《移动项目实践》实验报告——Android自定义控件 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

實驗目的

1、熟悉App開發(fā)經(jīng)常涉及的自定義控件相關技術,主要包括自定義視圖的過程與步驟、自定義動畫的原理與實現(xiàn)、自定義對話框的概念與示例、自定義通知欄的用法與定制;
2、熟悉四大組件之一的服務Service的生命周期與啟停方式;

實驗內(nèi)容

“手機安全助手”的設計與實現(xiàn)。
開發(fā)思路請參考:課件《第6章 自定義控件.pptx》
該項目采用多種自定義控件的相關技術,并同時運用多種存儲技術。通過該實戰(zhàn)項目的練習能夠加深自定義控件的用法理解,還能復習鞏固前兩章的存儲技術知識。
界面效果如下:

手機安全助手的流量頁面


上拉應用列表的流量頁面

實驗過程(實驗的設計思路、關鍵源代碼等)

源代碼:https://gitee.com/shentuzhigang/mini-project/tree/master/android-traffic

package io.shentuzhigang.demo.trafficimport android.os.Bundle import android.view.View import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.ListView import android.widget.Spinner import androidx.appcompat.app.AppCompatActivity import io.shentuzhigang.demo.traffic.R import io.shentuzhigang.demo.traffic.adapter.AppInfoAdapter import io.shentuzhigang.demo.traffic.util.AppUtil//import androidx.appcompat.app.AppCompatActivity; /*** Created by ouyangshen on 2017/10/14.*/ class AppInfoActivity : AppCompatActivity() {private var lv_appinfo // 聲明一個列表視圖對象: ListView? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_app_info)// 從布局文件中獲取名叫l(wèi)v_appinfo的列表視圖lv_appinfo = findViewById(R.id.lv_appinfo)initTypeSpinner()}// 初始化應用類型的下拉框private fun initTypeSpinner() {val typeAdapter = ArrayAdapter(this,R.layout.item_select, typeArray)val sp_list = findViewById<Spinner>(R.id.sp_type)sp_list.prompt = "請選擇應用類型"sp_list.adapter = typeAdaptersp_list.onItemSelectedListener = TypeSelectedListener()sp_list.setSelection(0)}private val typeArray = arrayOf("所有應用", "聯(lián)網(wǎng)應用")internal inner class TypeSelectedListener : AdapterView.OnItemSelectedListener {override fun onItemSelected(arg0: AdapterView<*>?, arg1: View, arg2: Int, arg3: Long) {// 獲取已安裝的應用信息隊列val appinfoList = AppUtil.getAppInfo(this@AppInfoActivity, arg2)// 構建一個應用信息的列表適配器val adapter = AppInfoAdapter(this@AppInfoActivity, appinfoList)// 給lv_appinfo設置應用信息列表適配器lv_appinfo!!.adapter = adapter}override fun onNothingSelected(arg0: AdapterView<*>?) {}}companion object {private const val TAG = "AppInfoActivity"} } package io.shentuzhigang.demo.trafficimport android.net.TrafficStats import android.os.* import android.widget.ListView import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import io.shentuzhigang.demo.traffic.R import io.shentuzhigang.demo.traffic.adapter.TrafficInfoAdapter import io.shentuzhigang.demo.traffic.util.AppUtil import io.shentuzhigang.demo.traffic.util.StringUtil/*** Created by ouyangshen on 2017/10/14.*/ class TrafficInfoActivity : AppCompatActivity() {private var tv_traffic // 聲明一個列表視圖對象: TextView? = nullprivate var lv_traffic: ListView? = nullprivate val mHandler = Handler() // 聲明一個處理器對象override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_traffic_info)tv_traffic = findViewById(R.id.tv_traffic)// 從布局文件中獲取名叫l(wèi)v_traffic的列表視圖lv_traffic = findViewById(R.id.lv_traffic)// 延遲50毫秒后開始刷新應用流量數(shù)據(jù)mHandler.postDelayed(mRefresh, 50)}// 定義一個刷新任務private val mRefresh = Runnable {val desc = String.format("""當前總共接收流量:%s其中接收數(shù)據(jù)流量:%s當前總共發(fā)送流量:%s其中發(fā)送數(shù)據(jù)流量:%s""".trimIndent(),StringUtil.formatData(TrafficStats.getTotalRxBytes()), // 獲取總共接收的流量數(shù)據(jù)StringUtil.formatData(TrafficStats.getMobileRxBytes()), // 獲取數(shù)據(jù)流量的接收數(shù)據(jù)StringUtil.formatData(TrafficStats.getTotalTxBytes()), // 獲取總共發(fā)送的流量數(shù)據(jù)StringUtil.formatData(TrafficStats.getMobileTxBytes())) // 獲取數(shù)據(jù)流量的發(fā)送數(shù)據(jù)tv_traffic!!.text = desc// 獲取已安裝的應用信息隊列val appinfoList = AppUtil.getAppInfo(this@TrafficInfoActivity, 1)for (i in appinfoList!!.indices) {val item = appinfoList[i]// 根據(jù)應用編號獲取該應用的接收流量數(shù)據(jù)// Android7之后,TrafficStats類的getUidRxBytes和getUidTxBytes只能查自身的流量。只有當前應用為系統(tǒng)應用之時,才能查其他應用的流量item!!.traffic = TrafficStats.getUidRxBytes(item.uid)appinfoList[i] = item}// 構建一個流量信息的列表適配器val adapter = TrafficInfoAdapter(this@TrafficInfoActivity, appinfoList)// 給lv_traffic設置流量信息列表適配器lv_traffic!!.adapter = adapter}companion object {private const val TAG = "TrafficInfoActivity"} } package io.shentuzhigang.demo.trafficimport android.annotation.SuppressLint import android.app.Activity import android.content.Intent import android.graphics.Color import android.graphics.Paint import android.os.* import android.view.View import android.widget.RelativeLayout import android.widget.TextView import io.shentuzhigang.demo.traffic.R import io.shentuzhigang.demo.traffic.MainApplication import io.shentuzhigang.demo.traffic.MobileConfigActivity import io.shentuzhigang.demo.traffic.adapter.TrafficInfoAdapter import io.shentuzhigang.demo.traffic.bean.AppInfo import io.shentuzhigang.demo.traffic.service.TrafficService import io.shentuzhigang.demo.traffic.util.AppUtil import io.shentuzhigang.demo.traffic.util.DateUtil import io.shentuzhigang.demo.traffic.util.SharedUtil import io.shentuzhigang.demo.traffic.util.StringUtil import io.shentuzhigang.demo.traffic.widget.CircleAnimation import io.shentuzhigang.demo.traffic.widget.CustomDateDialog import io.shentuzhigang.demo.traffic.widget.NoScrollListView import java.util.*/*** Created by ouyangshen on 2017/10/14.*/ @SuppressLint("DefaultLocale") class MobileAssistantActivity : Activity(), View.OnClickListener,CustomDateDialog.OnDateSetListener {private var tv_day: TextView? = nullprivate var rl_month: RelativeLayout? = nullprivate var tv_month_traffic: TextView? = nullprivate var rl_day: RelativeLayout? = nullprivate var tv_day_traffic: TextView? = nullprivate var nslv_traffic // 聲明一個不滾動列表視圖: NoScrollListView? = nullprivate var mDay // 選擇的日期= 0private var mNowDay // 今天的日期= 0private val traffic_month: Long = 0 // 月流量數(shù)據(jù)private var traffic_day: Long = 0 // 日流量數(shù)據(jù)private var limit_month // 月流量限額= 0private var limit_day // 日流量限額= 0private val line_width = 10override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_mobile_assistant)// 創(chuàng)建一個通往流量服務的意圖val intent = Intent(this, TrafficService::class.java)// 啟動指定意圖的服務startService(intent)initView()}// 初始化各視圖對象private fun initView() {tv_day = findViewById(R.id.tv_day)rl_month = findViewById(R.id.rl_month)tv_month_traffic = findViewById(R.id.tv_month_traffic)rl_day = findViewById(R.id.rl_day)tv_day_traffic = findViewById(R.id.tv_day_traffic)// 從布局文件中獲取名叫nslv_traffic的不滾動列表視圖nslv_traffic = findViewById(R.id.nslv_traffic)findViewById<View>(R.id.iv_menu).setOnClickListener(this)findViewById<View>(R.id.iv_refresh).setOnClickListener(this)// 從共享參數(shù)中讀取月流量限額limit_month = SharedUtil.Companion.getIntance(this)!!.readInt("limit_month", 1024)// 從共享參數(shù)中讀取日流量限額limit_day = SharedUtil.Companion.getIntance(this)!!.readInt("limit_day", 30)mNowDay = DateUtil.getNowDateTime("yyyyMMdd").toInt()mDay = mNowDayval day = DateUtil.getNowDateTime("yyyy年MM月dd日")tv_day?.setText(day)tv_day?.setOnClickListener(this)// 延遲500毫秒后開始刷新日流量數(shù)據(jù)mHandler.postDelayed(mDayRefresh, 500)}private val mHandler = Handler() // 聲明一個處理器對象// 定義一個日流量的刷新任務private val mDayRefresh = Runnable { refreshTraffic(mDay) }override fun onClick(v: View) {if (v.id == R.id.tv_day) { // 點擊了日期文本val calendar = Calendar.getInstance()// 彈出自定義的日期選擇對話框val dialog = CustomDateDialog(this)dialog.setDate(calendar[Calendar.YEAR], calendar[Calendar.MONTH],calendar[Calendar.DAY_OF_MONTH], this)dialog.show()} else if (v.id == R.id.iv_menu) { // 點擊了三點菜單圖標// 跳轉到流量限額配置頁面val intent = Intent(this, MobileConfigActivity::class.java)startActivity(intent)} else if (v.id == R.id.iv_refresh) { // 點擊了轉圈刷新圖標mDay = mNowDay// 立即啟動今天的流量刷新任務mHandler.post(mDayRefresh)}}override fun onDateSet(year: Int, month: Int, day: Int) {val date = String.format("%d年%d月%d日", year, month, day)tv_day!!.text = datemDay = year * 10000 + month * 100 + day// 選擇完日期,立即啟動流量刷新任務mHandler.post(mDayRefresh)}// 刷新指定日期的流量數(shù)據(jù)private fun refreshTraffic(day: Int) {val last_date = DateUtil.getAddDate("" + day, -1)// 查詢數(shù)據(jù)庫獲得截止到昨日的應用流量val lastArray: ArrayList<AppInfo>? =MainApplication.instance?.mTrafficHelper?.query("day=$last_date")// 查詢數(shù)據(jù)庫獲得截止到今日的應用流量val thisArray: ArrayList<AppInfo>? =MainApplication.instance?.mTrafficHelper?.query("day=$day")val newArray = ArrayList<AppInfo?>()traffic_day = 0// 截止到今日的應用流量減去截止到昨日的應用流量,二者之差便是今日的流量數(shù)據(jù)if (thisArray != null) {for (i in thisArray.indices) {val item = thisArray[i]if (lastArray != null) {for (j in lastArray.indices) {if (item.uid == lastArray[j].uid) {item.traffic -= lastArray[j].trafficbreak}}}traffic_day += item.trafficnewArray.add(item)}}// 給流量信息隊列補充每個應用的圖標val fullArray = AppUtil.fillAppInfo(this, newArray)// 構建一個流量信息的列表適配器val adapter = TrafficInfoAdapter(this@MobileAssistantActivity, fullArray)// 給nslv_traffic設置流量信息列表適配器nslv_traffic!!.adapter = adaptershowDayAnimation() // 顯示日流量動畫showMonthAnimation() // 顯示月流量動畫}// 顯示日流量的圓弧動畫private fun showDayAnimation() {rl_day!!.removeAllViews()val diameter = Math.min(rl_day!!.width, rl_day!!.height) - line_width * 2var desc = "今日已用流量" + StringUtil.formatData(traffic_day)// 創(chuàng)建日流量的圓弧動畫val dayAnimation = CircleAnimation(this@MobileAssistantActivity)// 設置日流量動畫的四周邊界dayAnimation.setRect((rl_day!!.width - diameter) / 2 + line_width,(rl_day!!.height - diameter) / 2 + line_width,(rl_day!!.width + diameter) / 2 - line_width,(rl_day!!.height + diameter) / 2 - line_width)val trafficM = traffic_day / 1024.0f / 1024.0fdesc = if (trafficM > limit_day * 2) { // 超出兩倍限額,則展示紅色圓弧進度val end_angle =(if (trafficM > limit_day * 3) 360 else (trafficM - limit_day * 2) * 360 / limit_day) as IntdayAnimation.setAngle(0, end_angle)dayAnimation.setFront(Color.RED, line_width.toFloat(), Paint.Style.STROKE)String.format("%s\n超出限額%s", desc,StringUtil.formatData(traffic_day - limit_day * 1024 * 1024))} else if (trafficM > limit_day) { // 超出一倍限額,則展示橙色圓弧進度val end_angle =(if (trafficM > limit_day * 2) 360 else (trafficM - limit_day) * 360 / limit_day) as IntdayAnimation.setAngle(0, end_angle)dayAnimation.setFront(-0x6700, line_width.toFloat(), Paint.Style.STROKE)String.format("%s\n超出限額%s", desc,StringUtil.formatData(traffic_day - limit_day * 1024 * 1024))} else { // 未超出限額,則展示綠色圓弧進度val end_angle = (trafficM * 360 / limit_day).toInt()dayAnimation.setAngle(0, end_angle)dayAnimation.setFront(Color.GREEN, line_width.toFloat(), Paint.Style.STROKE)String.format("%s\n剩余流量%s", desc,StringUtil.formatData(limit_day * 1024 * 1024 - traffic_day))}rl_day!!.addView(dayAnimation)// 渲染日流量的圓弧動畫dayAnimation.render()tv_day_traffic!!.text = desc}// 顯示月流量的圓弧動畫。未實現(xiàn),讀者可實踐之private fun showMonthAnimation() {rl_month!!.removeAllViews()val diameter = Math.min(rl_month!!.width, rl_month!!.height) - line_width * 2tv_month_traffic!!.text = "本月已用流量待統(tǒng)計"// 創(chuàng)建月流量的圓弧動畫val monthAnimation = CircleAnimation(this@MobileAssistantActivity)// 設置月流量動畫的四周邊界monthAnimation.setRect((rl_month!!.width - diameter) / 2 + line_width,(rl_month!!.height - diameter) / 2 + line_width,(rl_month!!.width + diameter) / 2 - line_width,(rl_month!!.height + diameter) / 2 - line_width)monthAnimation.setAngle(0, 0)monthAnimation.setFront(Color.GREEN, line_width.toFloat(), Paint.Style.STROKE)rl_month!!.addView(monthAnimation)// 渲染月流量的圓弧動畫monthAnimation.render()}companion object {private const val TAG = "MobileAssistantActivity"} }

實驗結果(實驗最終作品截圖說明)



實驗心得

1、熟悉App開發(fā)經(jīng)常涉及的自定義控件相關技術,主要包括自定義視圖的過程與步驟、自定義動畫的原理與實現(xiàn)、自定義對話框的概念與示例、自定義通知欄的用法與定制;
2、熟悉四大組件之一的服務Service的生命周期與啟停方式;

參考文章

總結

以上是生活随笔為你收集整理的《移动项目实践》实验报告——Android自定义控件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。