记录阅读 《Android 移动应用开发基础教程 微课版》 ISBN-978-7-115-47309-7 遇到的坑
android studio version 4.0.1
android SDK 10.0+
git仓库
相关文章Java http请求及常见数据交互格式处理
第一章 环境部署
jetbrains会帮你安好android studio
下载jetbrains toolbox
安装好后列表里有Android studio 点击安装即可,也可以切换版本,我这里用的时4.0.1版
进入Android studio 后如果你没有安装 android sdk 会让你安装 sdk 这里可能需要科学上网,另外后面程序下载依赖也需要用到科学上网。
gradle
引用包
1 implementation 'com.google.android.material:material:1.2.1'
设置版本号
1 2 3 4 5 6 7 8 9 10 defaultConfig { applicationId "com.example.moshuying" minSdkVersion 16 targetSdkVersion 30 versionCode 1 versionName "1.1.8" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" }
第二章 核心组件-活动
活动是什么
在android中运行任何应用都会看到不同的界面,这些界面及在界面中完成的各种操作,都通过活动完成
活动具有一下特点
可以通过返回键退出活动
可通过home建返回桌面
可在活动中启动另一个界面,此时按返回键可返回前一个活动
一个应用通常包含多个活动,活动之间相对独立,包含多个活动的应用,需要为其指定一个“主”活动,即启动应用时首先打开的活动。
Android 允许启动其他应用中的活动
活动的基本操作
为活动绑定自定义视图
通常在活动的onCreate()
方法中使用setContentView()
方法来为活动绑定一个视图
启动另一个活动
启动活动使用的是startActivity()
方法
1 2 3 4 5 6 7 8 9 10 public class MainActivity extends AppCompatActivity { @Override Button btnStartAnother = (Button)findViewById(R.id.btnStartAnother); btnStartAnother.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { startActivity(new Intent(MainActivity.this , BtnStartAnother.class)); } }); }
结束活动则调用finish()
,为按钮绑定事件监听器后点击按钮调用finish()
即可实现点击按钮退出活动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class BtnStartAnother extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.btn_start_activity); findViewById(R.id.destory).setOnClickListener(new View.OnClickListener(){ @Override public void onClick (View v) { finish(); } }); } }
在活动中使用Intent
显示Intent
直接忽略1-6步,手动创建java class后给class继承Activity
后会自动引入相关包,后面步骤同理
隐式Intent
这一步不要被android.support.v7.app.AppCompatActivity
吸引了注意力,后面的代码其实还是继承AppCompatActivity
,参照上面手动输入即可,android studio会自动引入,我这边自动引入为androidx.appcompat.app.AppCompatActivity
,如果过程中遇到问题,可以编辑build.gradle
**注意这个文件有两个,选(Module:app)**在dependencies
中加入一行compile 'com.android.support:appcompat-v7:26.+'
然后android studio会报错,照着报错修复即可,这一步可能会操作到Migrate to AndroidX
,android studio也会自动为你备份,不必担心。
这一步我在代码中做了各种分享功能 ,相关解析在这里 ,源代码参考链接
使用预定义操作
这里使用隐式Intent打开联系人信息的地方可能不太一样了,参考官网给出的样例
我的代码实现也是参考官网给出的样例,实现也很简单,这里代码去掉了注释,完整源代码在这里
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 findViewById(R.id.editContacts).setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { EditText emailAddress = (EditText) findViewById(R.id.email); EditText phoneNumber = (EditText) findViewById(R.id.phone); EditText name = (EditText) findViewById(R.id.name); Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION); intent.setType(ContactsContract.RawContacts.CONTENT_TYPE); intent.putExtra(ContactsContract.Intents.Insert.EMAIL, emailAddress.getText()) .putExtra(ContactsContract.Intents.Insert.EMAIL_TYPE,ContactsContract.CommonDataKinds.Email.TYPE_WORK) .putExtra(ContactsContract.Intents.Insert.PHONE, phoneNumber.getText()) .putExtra(ContactsContract.Intents.Insert.PHONE_TYPE,ContactsContract.CommonDataKinds.Phone.TYPE_WORK) .putExtra(ContactsContract.Intents.Insert.PHONETIC_NAME,name.getText()); startActivity(intent); } });
在活动之间传递数据
这一章节本身简单学习了活动的传递方式,重点了解如何在活动中传递数据。
书上的示例比较简单,new完intent后使用putExtra类似key,value的方式存进去就行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Intent intent = new Intent(JsonEdit.this ,Auto.class); Bundle bd = new Bundle(); TextView stringKey = findViewById(R.id.bundleKeyString); String string = stringKey.getText().toString(); bd.putString("String" ,string); TextView stringArray = findViewById(R.id.stringArray); bd.putStringArray("StringArray" ,stringArray.getText().toString().split("," )); startActivity(intent); final TextView editText = (TextView) findViewById(R.id.editTextTextMultiLine);editText.setText("{\\" name\\":\\" moshuying\\",\\" url\\":\\" moshuying.top\\",\\" email\\":\\" 1460083332 @qq \\",\\" github\\":\\" moshuying.top\\"}" ); Intent intent = new Intent(JsonEdit.this ,Auto.class); intent.putExtra("data" ,editText.getText().toString()); startActivity(intent);
当然在activity中传递数据的方法不止intent的,只是intent比较常用。
Activity之间传递数据的几种方式,参考自这篇文章 ,文章比较老了,方法竟然还是这些,甚至阿里巴巴的一些应用也是这样的。
Intent可以传递哪些类型的数据,来源
8种基本数据类型及其数组
String(String实现了 Serializable )/CharSequence实例类型的数据及其数组
实现了Parcelable的对象及其数组
实现了 Serializable 的对象及其数组
看到这里的时候我想到能在activity中获取数据,就能用这些数据动态创建页面,所以顺便看了下动态创建元素的办法。
实际上非常简单,先声明一个LinearLayout
1 2 3 4 5 6 <LinearLayout android:id ="@+id/auto_list_item" android:layout_width ="match_parent" android:layout_height ="wrap_content" android:orientation ="vertical" tools:ignore ="MissingConstraints" />
随后在对应的代码里获取到这个元素,再用addView()
即可动态添加
1 2 LinearLayout layout = findViewById(R.id.auto_list_item); layout.setOrientation(LinearLayout.VERTICAL);
想到动态添加元素就觉得可以根据后台请求的数据来生成各种界面,于是就又去了解了一下Android的网络请求方法相关解析 ,关键代码
既然都动态元素添加了,肯定会遇到添加的元素超过容器大小(超出屏幕)的情况,这时在对应的布局文件外面放一层ScrollView
即可解决问题。
1 2 3 4 5 6 7 8 9 10 11 12 <ScrollView android:layout_width ="match_parent" android:layout_height ="match_parent" android:scrollbars ="vertical" android:fadingEdge ="vertical" > <LinearLayout android:layout_width ="match_parent" android:layout_height ="match_parent" android:orientation ="vertical" > </LinearLayout > </ScrollView >
获取活动返回的数据
这一小节有一小部分不太一样的地方,启动活动变成了startActivityforResult
,活动返回时也要用setResult
设置返回结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 findViewById(R.id.getActivityBackData).setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent intent = new Intent(JsonEdit.this ,Auto.class); intent.putExtra("type" ,"callback" ); intent.putExtra("request_code" ,REQUEST_CODE); startActivityForResult(intent,REQUEST_CODE); } }); @Override protected void onActivityResult (int requestCode,int resultCode,Intent data) { super .onActivityResult(requestCode, resultCode, data); printState(); if (requestCode == REQUEST_CODE){ if (resultCode == RESULT_OK){ TextView textView = findViewById(R.id.showActivityBackData); textView.setText("从另一个Activity获取到返回值:" +data.getStringExtra("data" )); } } } protected void setCallback () { final EditText editText = new EditText(this ); editText.setText("这里填入活动返回的值" ); linearLayout.addView(editText); Button button = new Button(this ); button.setText("输入完成" ); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick (View v) { Intent resintent = new Intent(); resintent.putExtra("data" ,editText.getText().toString()); setResult(RESULT_OK,resintent); finish(); } }); linearLayout.addView(button); }
完整代码
活动的生命周期
这一章节有个检测活动生命周期的例子,这里我写了个直接获取的函数。
放到类里即可,记得把包名和类名换成你自己的即可
1 2 3 4 5 6 7 8 9 10 11 private static final String packageName = "com.example.moshuying" ;private static final String className = "JsonEdit" ;private static void printState () { StackTraceElement[] stes = Thread.currentThread().getStackTrace(); for (StackTraceElement ste : stes) { if ((ste.getClassName().equals(packageName+"." +className)) && (!ste.getMethodName().equals("printState" ))) { System.out.println("正在执行" + ste.getClassName().replace(packageName+"." ,"" ) + "." + ste.getMethodName()+"()" ); } } }
随后在生命周期对应的地方(其实其他类中的函数都可以)调用一下即可
1 2 3 4 5 @Override protected void onDestroy () { super .onDestroy(); printState(); }
最后的效果如图
代码设置界面元素
比较喜欢用代码生成界面,免去一堆xml难以寻找自己定义的一些东西,同时debug起来也比较麻烦,这里总结一些代码操作ui元素的代码片段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 final MaterialTextView textView = new MaterialTextView(this );RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(MATCH_PARENT,WRAP_CONTENT); layoutParams2.topMargin = 5 ; layoutParams2.addRule(RelativeLayout.BELOW,password.getId()); textView.setGravity(Gravity.CENTER_VERTICAL); textView.setLayoutParams(layoutParams2); textView.setId(View.generateViewId()); textView.setTextColor(Color.parseColor("#FF0000" )); relativeLayout.addView(textView);
给某个活动设置主题
1 2 <activity android:name =".StartupMode" android:theme ="@style/Theme.MaterialComponents" /> <activity android:name =".StartupMode" android:theme ="@style/Theme.MaterialComponents.Light" />
创建弹出式对话框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 protected void createDialog () { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this ); builder.setTitle("布局设置" ); builder.setIcon(R.drawable.ic_learning_material); LinearLayout layout = new LinearLayout(this ); layout.setOrientation(LinearLayout.VERTICAL); final EditText editText = new EditText(this );builder.setView(editText); MaterialTextView textView = new MaterialTextView(this ); textView.setText("请在下面输入1、2或其他数据" ); layout.addView(textView); layout.addView(editText); builder.setView(layout); builder.setPositiveButton("确认" , new DialogInterface.OnClickListener() { @Override public void onClick (DialogInterface dialog, int which) { changeLayout(editText.getText().toString()); } }); builder.setNegativeButton("取消" , new DialogInterface.OnClickListener() { @Override public void onClick (DialogInterface dialog, int which) { } }); builder.setCancelable(true ); AlertDialog dialog = builder.create(); dialog.setCanceledOnTouchOutside(true ); dialog.show(); }
问题
Android Studio –“Cannot resolve symbol”
Android Studio 无法识别同一个 package 里的其他类,将其显示为红色,但是 compile 没有问题。鼠标放上去后显示 “Cannot resolve symbol XXX”,重启 Android Studio,重新 sync gradle,Clean build 都没有用。
多半是因为 Android Studio 之前发生了错误,某些 setting 出了问题。解决方法如下:
点击菜单中的 “File” -> “Invalidate Caches / Restart”,然后点击对话框中的 “Invalidate and Restart”,清空 cache 并且重启。语法就会正确的高亮了。
open failed: EACCES (Permission denied)
在AndroidManifest.xml中声明了<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
,却还是报错open failed: EACCES (Permission denied)
。
问题的原因在于比如在安卓Q(10)开始,就采用存储的分区控制。
解决方法:只能通过手动打开权限,才能使用存储权限。 在AndroidManifest.xml的application标签下新增android:requestLegacyExternalStorage="true"
即可解决
参考文章
maven google 官方库 material 文档 Android Material Design全面解析(一)- MaterialButton篇 设置TextView文字居中,代码实现android:layout_gravity HTML 颜色名 Android 动态设置margin Android color(颜色) 在XML文件和java代码中 Android 显示、隐藏状态栏和导航栏 Android修改APP版本号 Android之设置EditText输入类型(setInputType()方法和android:inputType属性) Android Studio –“Cannot resolve symbol” 解决办法 Android10读写文件权限请求bug——open failed: EACCES (Permission denied)