安卓日常学习遇到的问题解决

使用SharedPreferences来存储数据

1.Context 类中的 getSharedPreferences()方法

第一个参数用于指定**SharedPreferences**文件的名称,如果指定的文件

不存在则会创建一个SharedPreferences 文件都是存放在/data/data//shared_prefs/

目录下的。第二个参数用于指定操作模式,目前只有 MODE_PRIVATE这一种模式可选,它是默

认的操作模式,和直接传入 0 效果是相同的,表示只有当前的应用程序才可以对这个

SharedPreferences文件进行读写。

2.Activity 类中的 getPreferences()方法

只接收一个操作模式参数,因为使用这个方法时会自动将当前活动的类名作为 SharedPreferences的文件名。

3.PreferenceManager类中的 getDefaultSharedPreferences()方法

这是一个静态方法,它接收一个Context*参数,并自动使用*当前应用程序的包名作为前缀

来命名 SharedPreferences文件

注:每个应用都有一个默认的配置文件preferences.xml,使用getDefaultSharedPreferences获取。

存储数据

(1) 调用SharedPreferences对象的 edit() 方法来获取一个 SharedPreferences.Editor

对象。

(2) 向 SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用

putBoolean() 方法,添加一个字符串则使用 putString() 方法,以此类推。

(3) 调用 apply()方法将添加的数据提交,从而完成数据存储操作。

获取数据

通过 getSharedPreferences()方法得到了 SharedPreferences 对象,然后分别调用它的 getString() 、 getInt() 和 getBoolean()方法,去获取前面所存储

removeAllViews()removeAllViewsInLayout() 的区别

removeAllViews(): ViewGroup中移除所有子视图

removeAllViewsInLayout() : ViewGroup的子类调用,移除自身的子视图,但在它能计算多少子视图被渲染前, 必须首先知道它在屏幕中尺寸。

在有些情况下,removeAllViews()能移除掉子视图,但removeAllviewsInLayout()移除不掉,因为子视图还未计算。

Fragment中addToBackStack()使用

addToBackStack(null) :调用该方法后,每次替换新的fragment,旧的fragment都被添加到回退栈中,当按返回键时,将之前的fragment恢复。可以用于将一个事务添加到返回栈中。

用于解决两个Fragment之间按返回键直接退出activity的问题(即无法返回上一个Fragment)

ViewPager(视图滑动切换工具)

ViewPager就是一个简单的页面切换组件,我们可以往里面填充多个View,然后我们可以左 右滑动,从而切换不同的View,我们可以通过setPageTransformer()方法为我们的ViewPager设置切换时的动画效果。

让SDK版本高于27的项目可以使用http请求,解除安全限制。

创建xml文件,标签为网络安全设定

1
2
3
4
5
<network-security-config>
<!--禁用掉明文流量请求的检查-->
<base-config cleartextTrafficPermitted="true" />

</network-security-config>

还需要在AndroidManifest.xml文件中进行引用

1
android:networkSecurityConfig="@xml/network_security_config"

优化切换动画

自定义ViewPage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class FixedViewPager extends ViewPager {

public FixedViewPager(@NonNull Context context) {
super(context);
}

public FixedViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}

//将滑动的动画效果关闭
@Override
public void setCurrentItem(int item) {
super.setCurrentItem(item,false);
}
}

super.setCurrentItem(item,false);当第二个参数为false时,切换页面时的缓慢的滑动效果消失,变成快速切换。

1
2
//将fragment预加载进来,防止出现异常
viewPager.setOffscreenPageLimit(mFragments.size());

播放器解决全屏时出现闪退问题,让其生命周期不发生变化

1
2
3
4
5
6
<!--解决全屏时出现闪退问题,让其生命周期不发生变化-->
<activity android:name=".activity.HomeActivity"

android:configChanges="orientation|screenSize|keyboardHidden"
android:screenOrientation="portrait">
</activity>

使用handle完成与主线程沟通。和runOnUiThread是同样的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//因为数据需要在主线程刷新,所以使用handle与主线程沟通,当需要时就发送msg请求传入数据
private Handler mhandle = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
videoAdapter.setDatas(datas);
//通知recycleView 去刷新页面,刷新数据
videoAdapter.notifyDataSetChanged();
break;
}
}
};

登录成功后,要清除回退栈,防止点击回退键回到登录页面。

1
2
3
4
5
6
7
8
9
10
11
12
//清除回退堆栈
//Intent.FLAG_ACTIVITY_NEW_TASK创建一个新栈
navigateToWithFlag(HomeActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
showToastSync("登录成功");

//清除回退堆栈
//Intent.FLAG_ACTIVITY_NEW_TASK创建一个新栈
public void navigateToWithFlag(Class cls, int flags) {
Intent intent = new Intent(mContext, cls);
intent.setFlags(flags);
startActivity(intent);
}

安卓运行报错114:25: AAPT: error: resource android:attr/lStar not found.

原因是一开始创建的app中的**build.gradele**中的版本依赖过高。下面是较低版本。

1
2
3
4
5
6
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

RecycleView中没有针对item的点击事件,需要自行创建一个接口。特别是需要跳转到activity情况

写一个接口

1
2
3
public interface OnItemClickListener {
void onItemClick(Serializable obj);
}

在adapter中声明该OnItemClickListener接口类,设置一个传参方法。

1
2
3
4
private OnItemClickListener mOnItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}

在控件点击事件中调用该接口类方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
newsAdapter.setOnItemClickListener(new NewsAdapter.OnItemClickListener() {
@Override
public void onItemClick(Serializable obj) {
//showToast("点击");
NewsEntity newsEntity = (NewsEntity) obj;
//记载h5链接
String url = "http://10.211.29.252:8089/newsDetail?title=" + newsEntity.getAuthorName();
Bundle bundle = new Bundle();
bundle.putString("url", url);
//传递bundle数据到WebActivity中,用于使用url打开h5页面
navigateToWithBundle(WebActivity.class, bundle);
}
});
1
add(0, node.val);在列表的指定位置插入指定元素(可选操作)。将当前处于该位置的元素(如果有的话)和所有后续元素向右移动(在其索引中加 1)。

AndroidMainfest.xml中的android.support.v4.content.FileProvider爆红

使用打开相机拍照时, 使用到FileProvider则是一种特殊的内容提供器,它使用了和内容提供器类似的机制来对数据进行保护,可以选择性地将封装过的 Uri共享给外部。使用FileProvider时在AndroidMainfest.xml中注册

解决方式:需要将其换成androidx.core.content.FileProvider

1
2
3
4
5
6
7
8
9
<provider
android:authorities="com.example.cameraalbumtest.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>

file_paths.xml中的path爆红(不算问题)

这里爆红不影响程序运行。

去掉EditText的下划线直接将android:background="@null"

安卓按比例进行布局

将子View的宽度或是高度设为0,然后为子View设置不同权重(weight) ,这样子View的大小就会权值成比例。

本例使用横向LinearLayoutLinearLayoutandroid:layout_width=”match_parent”,表示将使用整个屏幕宽度。

对于LinearLayout的几个子View,将它们的宽度都定义为0,android:layout_width="0dip",然后使用layout_weight 为每个View指定宽度比例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Button
android:id="@+id/note"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="1"
android:text="笔记">
</Button>

<Button
android:id="@+id/target"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_weight="1"
android:text="代办" />

Android EditText 输入回车符自动跳转至下一个EditText

在输入信息时,在EditText控件输入回车键,经常不是换行二十让光标直接跳转到下一个编辑框。该功能主要用到了文本监听器借口**TextWatcher**,主要监听用户是否输入回车符,若是监听到已输入回车符,就自动将焦点移动到下一个控件,从而实现回车符自动跳转的要求

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
public class MainActivity extends AppCompatActivity {
EditText editText1,editText2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText1=(EditText)findViewById(R.id.editText1);
editText2=(EditText)findViewById(R.id.editText2);

editText1.addTextChangedListener(new JumpTextWatcher());
}


private class JumpTextWatcher implements TextWatcher{

@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {
String str=s.toString();
if (str.indexOf("\r")>=0 || str.indexOf("\n")>=0){
//发现输入回车符或换行符
editText1.setText(str.replace("\r","").replace("\n",""));
//去掉回车符和换行符
editText2.requestFocus();
//让editText2获取焦点
editText2.setSelection(editText2.getText().length());
//若editText2有内容就将光标移动到文本末尾
}
}
}
}

如何用su命令切换成超级管理员

输入adb.shell如何显示$代表我们是普通管理员,需要通过su命令切换成超级管理员。但是提示/system/bin/sh: su: not found

其实这个问题是因为我们用的模拟器,带有了Googel play是不允许获得管理员权限。下载一个Target是Google APIS的模拟器,打开AVD Manager,选择创建一个新的设备并给它更换镜像。

Android中删除EditText中内容时报SPAN_EXCLUSIVE_EXCLUSIVE spans cannot have a zero length

在使用Android中的EditText时,若在对应APP窗口中删除文本框中的内容或移动光标位置时,会出现如下错误:

在对应EditText下添加 android:inputType=“textNoSuggestions”属性即可

在使用DrawerLayout关闭菜单时

1
closeDrawer(@DrawerLayout.EdgeGravity int gravity)

Gravity.LEFT 移动左侧抽屉或 Gravity.RIGHT 向右移动。也可以使用 GravityCompat.STARTGravityCompat.END

安卓启动不了,安卓必须要先安装好jdk并配置好环境

NDK版本对不上,只需在SDK Manager 中添加即可

Error: open failed: ENOENT (No such file or directory)

当用户从图库中选择文件时,不能保证选择的文件是由其他应用程序添加或编辑的。因此,如果用户选择了一个属于另一个应用程序的文件,我们就会遇到权限问题。对此的快速解决方法是在 AndroidManifest.xml 文件中添加以下代码:

1
2
3
4
5
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>

Android之NetworkOnMainThreadException异常

网络请求在MainThread中产生的异常,网络请求尽量用RxJava异步请求。

抛出java.lang.IllegalArgumentException: Missing either @POST URL or @Url parameter.

**该异常是指@**POST内容为空

解决办法直接在其中加上 没有数据就加 . 或者 /

1
2
3
@POST(".")
Observable<FeedBackBean> feedbacklist
(@Field("postinfo") String postinfo);

融云自定义会话界面中的toolbar如果要自定义的话,必须要复制包里面toolbar的item布局代码到自定义的会话活动中并让其不可见。

viewpage里面可以嵌套recycleview,可以单独创建出来一个view出来,然后adapter中放入new出来的recycleview。

recycleView置顶item

1
2
3
mLinearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mLinearLayoutManager.scrollToPositionWithOffset(position, 0);

switch控件使用

1
2
3
4
5
6
7
8
9
10
11
// 点击事件
mSwitchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// 选中状态 (更改显示信息, 保存设置到sp...)
} else {
// 未选中状态 (更改显示信息, 保存设置到sp...)
}
}
});

出现Cannot resolve symbol ‘@+id/follow_list’ ,直接rebuild项目即可

Android volley error: “Trust anchor for certification path not found”, only in real device, not emulator

该错误是对于一些手机再进行使用选择照片功能时,出现的SSL证书错误,只需要允许通过所有证书即可

将这段代码添加到程序中,在onCreate之后调用

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
/**
* Enables https connections
*/
@SuppressLint("TrulyRandom")
public static void handleSSLHandshake() {
try {
TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}

@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}

@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};

SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
} catch (Exception ignored) {
}
}

请求码为-2则表示传入的参数有问题,一般要传入json格式。from-data在登录的时候才使用。

list接口之后所有方法都不能传入null

integer 相比于int类型可以为空,在RxJava中不允许integer 或任意数据为空的情况

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
Observable.just(1)
.subscribeOn(Schedulers.newThread())
.map(new Func1<Integer, Boolean>() {
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public Boolean call(Integer integer) {
tempFriendsListResults = GetFriendsListProtocol.getFriendsList(new FriendsList(20, page));
Log.v("loadData", "运行" + page);
if (tempFriendsListResults != null) {
if (friendsListResults != null && currentPage == 1) {
friendsListResults.clear();
}
Log.v("loadDataInside", "运行");
//添加数据并排序
//排序逻辑:先按照状态排,其次按首字母
Map<String, List<FriendsListResult>> map = tempFriendsListResults.stream().sorted(Comparator.comparing(FriendsListResult::getNickname))
.collect(Collectors.groupingBy(FriendsListResult::getOnlineStatus, Collectors.toList()));

Log.v("loadDataInside", "sort");
if (map.get("Online") != null) {
//添加数据
friendsListResults = map.get("Online");
if (map.get("Incall") != null) {
friendsListResults.addAll((Objects.requireNonNull(map.get("Incall"))));
}
if (map.get("Busy") != null) {
friendsListResults.addAll(Objects.requireNonNull(map.get("Busy")));
}
if (map.get("Offline") != null) {
friendsListResults.addAll(Objects.requireNonNull(map.get("Offline")));
}

} else if (map.get("Incall") != null) {
friendsListResults = map.get("Incall");
if (map.get("Busy") != null) {
friendsListResults.addAll(Objects.requireNonNull(map.get("Busy")));
}
if (map.get("Offline") != null) {
friendsListResults.addAll(Objects.requireNonNull(map.get("Offline")));
}

} else if (map.get("Busy") != null) {
friendsListResults = map.get("Busy");
if (map.get("Offline") != null) {
friendsListResults.addAll(Objects.requireNonNull(map.get("Offline")));
}

} else if (map.get("Offline") != null) {
Log.v("loadDataInside", "inside?");
friendsListResults = map.get("Offline");
}
if (tempFriendsListResults != null) {
tempFriendsListResults.clear();
}
return true;
} else {
Log.v("loadDataInside", "数据为空?");
return false;
}
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
@Override
public void call(Boolean aBoolean) {
if (aBoolean) {
if (followAdapter != null) {
followAdapter.notifyDataSetChanged();
Log.v("loadDataInside", "notify");
}
Log.v("loadDataInside", "refresh");
followedXRefreshView.stopRefresh(true);
followedXRefreshView.stopLoadMore();
} else {
followedXRefreshView.stopRefresh(false);
followedXRefreshView.stopLoadMore();
}
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Log.v("loadDataInside", throwable.getMessage());
followedXRefreshView.stopRefresh(false);
followedXRefreshView.stopLoadMore();
}
});

Android Jetpack:

从androidx包中导入的类引用Jetpack库。在构建中对Jetpack的依赖。Gradle文件也从androidx开始。

kotlin 中关键字 lateinit

需要在最外面声明一个变量时,如果直接将该变量设置为空,则每次初始化该变量时都需要判空。如果直接声明成lateinit的变量,如果在整个代码里面都没有进行任何的初始化,那么能否编译通过?如果你加上了lateinit关键字,kotlin的编译器不会做这种检查。如果你将变量声明为lateinit,它就认为你肯定会初始化,至于你是怎么初始化它的,它就不管了

总结:

  1. lateinit 延迟加载

2.lateinit 只能修饰, 非kotlin基本类型

3如果你的代码真的显示初始化了lateinit变量,而又抛出了UninitializedPropertyAccessException异常, 因为你恰好将变量初始化为null了

因为Kotlin会使用null来对每一个用lateinit修饰的属性做初始化,而基础类型是没有null类型,所以无法使用lateinit。

使用 Stack Overflow程序员社区搜索问题

进入Stack Overflow。

在搜索框中输入“如何通过Wi-Fi设置和使用ADB ?”你可以不注册就在Stack Overflow上搜索,但如果你想发布一个新问题或回答一个问题,你需要注册。

在搜索框中输入[android]。括号中的[]表示您想要搜索已标记为关于Android的文章。

您可以结合标签和搜索词,使您的搜索更具体。试试这些搜索:

(android)和(layout)

(android)“hello world”

在项目级的build gradle中可以查看当前项目是app类型还是library类型,library类型就必须要依赖进入项目中才可以使用,等同sdk

Kotlin插件更新错误

please update kotlin plugin

正常在使用if判断更改界面文字样式等,记得要加上else判断特殊情况,防止出错。即使是空的也行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (position < promotionList.size() && !promotionList.isEmpty()) {
//格式化
String date = DateUtil.getDate("hh:mm:ss", goodsResults.get(promotionList.get(position)).getSurplusMillisecond());
holder.tag.setText(date);
//设置背景
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);
holder.tag.setBackground(QuickDrawable.create()
.bgColor(gradientDrawable.getOrientation(), new int[]{Color.parseColor("#513dfb"), Color.parseColor("#730ffe")})
.corner(DeviceUtils.dip2px(8), 0, 0, DeviceUtils.dip2px(8))
.build());
//促销商品背景
holder.mBgLayout.setBackgroundResource(R.drawable.shape_store_blue);
}else {
//这里多做个if,就不会出现不应该被渲染的item被渲染
}

在需要实时刷新改变的recycleView等列表,因为其一般只显示能看见的几个item,因此他的position值是一直改变的。

因此最好在绑定数据时,先设置一个默认的界面数据,在进入判断时,再进行改变

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
43
44
//绑定数据
//默认数据,防止因为position改变而使其他item改变
holder.mBgLayout.setBackgroundResource(R.drawable.shape_store_white);
holder.discount.setText("");
holder.tag.setText("");
holder.tag.setBackground(null);
holder.icon.setText(goodsResults.get(position).getExchangeCoin().toString());
holder.price.setText("$" + goodsResults.get(position).getPrice().toString());
if (!goodsResults.get(position).getDiscount().toString().equals("0.00")) {
holder.discount.setText(goodsResults.get(position).getDiscount().toString() + "%off");
}

if (position < promotionList.size() && !promotionList.isEmpty()) {
//格式化
String date = DateUtil.getDate("hh:mm:ss", goodsResults.get(promotionList.get(position)).getSurplusMillisecond());
holder.tag.setText(date);
//设置背景
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);
holder.tag.setBackground(QuickDrawable.create()
.bgColor(gradientDrawable.getOrientation(), new int[]{Color.parseColor("#513dfb"), Color.parseColor("#730ffe")})
.corner(DeviceUtils.dip2px(8), 0, 0, DeviceUtils.dip2px(8))
.build());
//促销商品背景
holder.mBgLayout.setBackgroundResource(R.drawable.shape_store_blue);
}



//设置左上角的标签
if (goodsResults.get(position).getTags() != null && !goodsResults.get(position).getPromotion()) {
//标签不能为空
if (!goodsResults.get(position).getTags().isEmpty()) {
holder.tag.setText(goodsResults.get(position).getTags());
//设置背景
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setOrientation(GradientDrawable.Orientation.TOP_BOTTOM);
holder.tag.setBackground(QuickDrawable.create()
.bgColor(gradientDrawable.getOrientation(), new int[]{Color.parseColor("#513dfb"), Color.parseColor("#730ffe")})
.corner(DeviceUtils.dip2px(8), 0, 0, DeviceUtils.dip2px(8))
.build());
Log.v("tagbiaoqian",position+" "+goodsResults.get(position).getTags());
}
}

android studio 在新版本必须要求使用Gradle JDK 11 版本,否则会出现报错

1
execution failed for task ':app:kapthost release debug kotlin'. > a failure occurred while executing org.jetbrains.kotlin.gradle.internal.kaptwithoutkotlinctask$kaptexecutionworkaction > java.lang.reflect.invocationtargetexception (no error message)

Android开发中Button背景颜色不能修改问题及解决方法

问题原因

出现该问题的原因主要是因为使用Android Studio 4.1之后的版本进行开发时创建的项目默认的主题所有Button都是Material类型的Button,默认使用主题色,所以想要修改颜色,就要把默认主题给关了或替代了

解决方法:

方式一:

<Button

        android:id=”@+id/button4”

改为——–>

<android.widget.Button

        android:id=”@+id/button4”

方式二:

找到temes.xml文件

将这段代码:

1
<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar">

修改为:

1
<style name="Theme.MyApplication" parent="Theme.MaterialComponents.DayNight.DarkActionBar.Bridge">