3. 请详细说明“我的垃圾分类APP”的功能、出现的关键问题及解决方案
(1)主要功能:
①登录预览页:
打开APP之后不会立即进入,而是会展示五秒封面之后再进入APP。
②主页:
③语音输入
可以通过语音完成垃圾名的输入:
④图片识别:
可以通过识别上传的图片来完成垃圾的识别
⑤版本更新:
此处我通过代码托管平台进行托管,完成版本的更新。
⑥APP评价:
可以通过设置->评价APP链接APPStore对APP进行打分。
由于本APP未发布,故显示No Data
⑦主页上的几个按键:
(2)遇到的问题及解决办法:
①**Q:ViewGroup为什么不会调用onDraw
A: 正常情况下,我们重写LinearLayout的onDraw方法,它是不会被调用的下面将分析原因并做解释。**
在完成主页面功能栏的布局中,遇到了问题重写LinearLayout的onDraw方法时不会被调用,从而导致错误。
<com.test.demo.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_absolute" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#FF000000"> </com.test.demo.MyLinearLayout>
大概的代码层次是,MyLinearLayout从LinearLayout派生出来,然后在程序中重载OnDraw。但是,onDraw不会被调用。我们可能会遇到这个问题:如果不给LinearLayout设置一个背景,系统是不会调用onDraw时,也就是说,我们重写的onDraw是不会调用的。当设置一个背景后,onDraw就会被调用。
造成这种现象的原因是继承自LinearLayout,而LinearLayout这是一个容器,ViewGroup本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是如果给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为LinearLayout上面有东西可画了,因此会调用onDraw方法。
我们可以仔细分析View的源码,它有一个方法View#draw(Canvas)方法,这里面有两个地方调用onDraw,它的条件都是:if (!dirtyOpaque) onDraw(canvas);
也就是说,如果dirtyOpaque是true的话,onDraw就不会调用,而dirtyOpaque的值的计算代码如下:
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
此外,View还提供了一个重要的方法:setWillNotDraw:
public void setWillNotDraw(boolean willNotDraw) { setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); }
通过查阅资料,得知如果想重写onDraw,应调用这个方法来清除flag,所以如果想要重写LinearLayout的onDraw,可以在其构造方法中调用setWillNotDraw方法。 在ViewGroup初始他时,它调用了一个私有方法:initViewGroup,其中setFlags(WILL_NOT_DRAW, DRAW_MASK); 相当于调用了setWillNotDraw(true),因此对于ViewGroup,它就认为是透明的了。
如果我们想要重写onDraw,就需要调用setWillNotDraw(false)
综上所述:
i.ViewGroup默认情况下,会被设置成WILL_NOT_DRAW,这是从性能考虑,这样一来,onDraw就不会被调用了。
ii.如果要重写一个ViweGroup的onDraw方法,有两种方法:在构造函数里面,给其设置一个颜色,如#00000000或在构造函数里面,调用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。
②加载条死循环
我的透明进度条原理是不断重绘一个背景图片,然后使用Matrix旋转图片,原先的代码是死循环,代码如下:
protected Void doInBackground(Void... params) { while( true ) { try { Thread.sleep(100); } catch(InterruptedException e) { e.printStackTrace(); } publishProgress(); } return null; }
这样就有一个bug,因为是死循环,当ProgressDialog被dismiss掉之后这个循环并不会销毁,而是一直在循环,这样造成的一个bug就是使用了透明进度条之后侧滑菜单点击就没有反应了,并且,透明进度条在Activity下次启动并不会有旋转的效果,这里修改的办法是将这个while(true)改成判断while( mLoop ) 然后重写ProgressDialog的dismiss()方法:
public void dismiss() { super.dismiss(); mProgressView.setLoop(false); }
③使用Logcat进行debug
Logcat 是一个命令行工具,用于转储系统消息日志,包括设备抛出错误时的堆栈轨迹,以及从应用使用 Log 类写入的消息。
Android 日志记录系统是系统进程 logd 维护的一组结构化环形缓冲区。这组可用的缓冲区是固定的,并由系统定义。最相关的缓冲区为:main(用于存储大多数应用日志)、system(用于存储源自 Android 操作系统的消息)和 crash(用于存储崩溃日志)。每个日志条目都包含一个优先级(VERBOSE、DEBUG、INFO、WARNING、ERROR 或 FATAL)、一个标识日志来源的标记以及实际日志消息。
日志记录系统的主接口是共享库 liblog 及其头文件 <android/log.h>。所有语言特定的日志记录工具最终都会调用函数 __android_log_write。默认情况下,它会调用函数 __android_log_logd_logger,该函数使用套接字将日志条目发送到 logd。从 API 级别 30 开始,可通过调用 __android_set_log_writer 更改日志记录函数。
通过使用Logcat可以比较方便的获得程序的运行情况
例如如下情况:
④ScrollView和ListView两个View都有滚动的效果,在嵌套使用时起冲突的问题
在scrollview里面嵌套了一个listview ,通过设置一个方法设置了listview的高度 现在的情况就是进到这个界面的时候看到的不是最上面 而是中间 ,该问题的解决办法为:
mScrollView.smoothScrollTo(0,20);
如此以上代码还是无效, 在代码里去掉listview的焦点 lv.setFocusable(false)即可。
或与EditText一样,在父元素的属性下面下下面这两行即可,不用代码设置。亲测可用。
android:focusableInTouchMode=“true”
android:focusable=“true”
/** * 重新计算ListView的高度,解决ScrollView和ListView两个View都有滚动的效果,在嵌套使用时起冲突的问题 * @param listView */ public void setListViewHeight(ListView listView) { // 获取ListView对应的Adapter ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) { return; } int totalHeight = 0; for (int i = 0, len = listAdapter.getCount(); i < len; i++) { // listAdapter.getCount()返回数据项的数目 View listItem = listAdapter.getView(i, null, listView); listItem.measure(0, 0); // 计算子项View 的宽高 totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度 } ViewGroup.LayoutParams params = listView.getLayoutParams(); params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1)); listView.setLayoutParams(params); }
⑤Theme.AppCompat.Light报错
在styles.xml中<style name=“AppBaseTheme” parent=“Theme.AppCompat.Light”>提示如下错误,这是版本问题。
error: Error retrieving parent for item: No resource found that matches the given name ‘Theme. AppCompat. Light’.
解决方法:
打开任意一个.java文件,输入android.R.style.这时你会看到一个提示列表,仔细看看,再对比一下,看哪个与原有的最接近就选择哪个。用将下划线替换成"."就可了。
将<style name=“AppBaseTheme” parent=“Theme.AppCompat.Light”>改为<style name=“AppBaseTheme” parent=“android:Theme.Light”>同理,将<style name=“AppBaseTheme” parent=“Theme.AppCompat.Light.DarkActionBar”>改为<style name=“AppBaseTheme” parent=“android:Theme.Holo.Light.DarkActionBar”>。
⑥偶发AndroidStudio进入界面卡死,无法进入
经过上网查阅资料后,发现是如下几个原因,并附上解决办法:
如果是因为内存不足:修改studio64.exe.vmoptions studio.exe.vmoptions 2个文件,将文件内分配内存调大即可。
或者是加载时间过长导致一直处于加载状态,此时只需打开AS安装目录下的bin内的idea.properties文件,并在最后加上disable.android.first.run=true
三、实验总结
本次大作业中,我使用了数据持久化与共享技术,网络技术——API接口,安卓权限申请,广播系统,安卓动态绘图,Intent传值完成Activity交互,Fragment碎片布局,Logcat调试,LinearLayout布局。并自学了如何将代码打包成APK,参考网上资料学习了怎么对APP完成版本更新与调用商店进行评分。通过自学第14章,对工程代码进行分层次分结构存储。通过学习第12章也尝试使用了一些精美的UI动画使得APP更美观。
此外,我也自学了Java的相关知识,如线程,单例等等,这些都是我之前没用过也没尝试过的。本次大作业极大的提高的我的编程能力与实际应用能力。由于疫情在家期间自学了Java,在本课程的每次实验乃至本次大作业中,并没有遇到比较多的问题。
通过一学期对《移动设备交互应用》这门课的学习,我的Java代码技术与自学能力得到了很大提升,我也学会了如何将一些点子转化为代码并进行实现。