记录阅读 《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"
即可解决
参考文章