Android 学习笔记
Log
public static final String TAG = "myRecyclerTag";
1、常用Log等级
Android系统为开发者提供了良好的日志工具android.util.Log,常用的方法有如下5个,将log的输出等级也依次指定了5个级别:
- Log.v:这里的v代表Verbose啰嗦的意思,对应的log等级为VERVOSE。采用该等级的log,任何消息都会输出。
- Log.d:这里的d代表Debug调试的意思,对应的log等级为DEBUG。采用该等级的log,除了VERBOSE级别的log外,剩余的4个等级的log都会被输出。
- Log.i:这里的i代表information,为一般提示性的消息,对应的log等级为INFO。采用该等级的log,不会输出VERBOSE和DEBUG信息,只会输出剩余3个等级的信息。
- Log.w:w代表warning警告信息,一般用于系统提示开发者需要优化android代码等场景,对应的等级为WARN。该级别log,只会输出WARN和ERROR的信息。
- Log.e:e代表error错误信息,一般用于输出异常和报错信息。该级别的log,只会输出该级别信息。一般Android系统在输出crassh等致命信息的时候,都会采用该级别的log。
生命周期
控件
//R 代表resource
Button myButton = findViewById(R.id.my_button);
textView.setText("pause")
//事件监听
//button
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
textView.setText("左边");
}
});
//switch
aSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
textView.setText("开");
}else{
textView.setText("关");
}
}
});
//屏幕旋转前保存数据
@Override
public void onSaveInstanceState(@NonNull Bundle outState, @NonNull PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
outState.putString("KEY",textView.getText().toString());
}
jetpack
- liveData
- Room
- ViewModel
Intent
Android组件系列----Intent详解 - 千古壹号 - 博客园 (cnblogs.com)
Intent的概念:
- Android中提供了Intent机制来协助应用间的交互与通讯,或者采用更准确的说法是,Intent不仅可用于应用程序之间,也可用于应用程序内部的activity, service和broadcast receiver之间的交互。Intent这个英语单词的本意是“目的、意向、意图”。
- Intent是一种运行时绑定(runtime binding)机制,它能在程序运行的过程中连接两个不同的组件。通过Intent,你的程序可以向Android表达某种请求或者意愿,Android会根据意愿的内容选择适当的组件来响应。
Context
Context提供了关于应用环境全局信息的接口。它是一个抽象类,它的执行被Android系统所提供。它允许获取以应用为特征的资源和类型,是一个统领一些资源(应用程序环境变量等)的上下文。也就是说Context描述一个应用程序环境的信息(即上下文);是一个抽象类,Android提供了该抽象类的具体实现类;通过它我们可以获取应用程序的资源和类(包括应用级别操作,如启动Activity,发广播,接受Intent等)。
单位 dp、sp以及屏幕像素密度
dp
英文density-independent pixel的缩写形式,意为密度无关像素。在设置边距、内边距或任何不打算按像素值指定尺寸的情况下,通常都使用dp这种单位。如设备屏幕密度较高,密度无关像素会相应扩展至整个屏幕。1dp单位在设备屏幕上总是等于1/160英寸。使用dp的好处是,无论屏幕密度如何,总能获得同样的尺寸。
sp
英文scale-independent pixel的缩写形式,意为缩放无关像素。它是一种与密度无关的像素,这种像素会受用户字体偏好设置的影响。我们通常会使用sp来设置屏幕上的字体大小。
pt、mm、in
类似于dp的缩放单位。允许以点(1/72英寸)、毫米或英寸为单位指定用户界面尺寸。但在实际开发中不建议使用这些单位,因为并非所有设备都能按照这些单位进行正确的尺寸缩放配置。
4种基本布局
线性布局 LinearLayout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
- android:orientation="horizontal" 水平
- android:orientation="vertical" 垂直
相对布局 RelativeLayout
<RelativeLayout xmLns:android="http://schemas.android.com/apk/res/android"
android:layout width="match parent"
android:layout_height="match parent">
</RelativeLayout>
帧布局 FrameLayout
所有的控件默认放在布局的左上角
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</FrameLayout>
百分比布局
<androidx.percentlayout.widget.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button1"
android:text="Button 1"
android:layout_gravity="left|top"
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
/>
</androidx.percentlayout.widget.PercentFrameLayout>
AndroidManifest
application中android:name的作用
AndroidManifest.xml 中 application 有一个属性是 android:name ,它是用来app启动时来关联一个application的,默认关联的是android.app.Application
当app启动时,会默认创建一个application的实例 ,当在Activity中调用getApplication()方法时 ,就会返回这个实例,所以这个 android:name 指定的类就有点似于全局变量的作用吧 , 用来存储数据供给整个 Activity 使用
Toast
一般底部弹出
Toast.makeText (getApplicationcontext(),"Toast",Toast.LENGTH_SHORT).show();
居中弹出
1.首先将toast的内容找到 2.设置这个toast的布局 3.进行show 4.
Toast toastCenter Toast.makeText(getApplicationcontext(),"居中Toast",Toast.LENGTH_SHORT);//没有shoW
toastCenter.setGravity(Gravity.CENTER,0,0);
toastCenter.show();
封装方法
public class ToastUtil {
public static Toast mToast;
public static void showMsg(Context context,String msg){
if(mToast ==null){
mToast =Toast.makeText(context,msg,Toast.LENGTH_SHORT);
}else{
mToast.setText(msg);
}
mToast.show();
}
}
//使用方法
ToastUtil.showMsg(MainActivity.this,"text");
ToastUtil.showMsg(getApplicationContext(),"text")
AlertDialog 弹窗
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("确认?");
builder.setPositiveButton("确认删除?", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int arg1) {
// TODO Auto-generated method stub
dialog.dismiss();
int row = mNoteDbOpenHelper.deleteFromDbById(note.getId());
if (row > 0) {
mAdapter.removeData(position);
}
ToastUtil.toastShort(getContext(), "confirm+");
}
});
builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
dialog.dismiss();
ToastUtil.toastShort(getContext(), "cancel+");
}
});
builder.create().show();
Activity
Activity的4种启动模式
android:launchMode属性
- standard:标准模式,默认
- singleTop:Task栈顶复用模式
- singleTask:Task栈内复用模式
- singleInstance:全局单例模式
可以在AndroidManifest.xml中activity标签的属性android:launchMode中设置该activity的加载模式。
- standard模式:默认的模式,以这种模式加载时,每当启动一个新的活动,必定会构造一个新的Activity实例放到返回栈(目标task)的栈顶,不管这个Activity是否已经存在于返回栈中;
- singleTop模式:如果一个以singleTop模式启动的activity的实例已经存在于返回桟的桟顶,那么再启动这个Activity时,不会创建新的实例,而是重用位于栈顶的那个实例,并且会调用该实例的onNewIntent()方法将Intent对象传递到这个实例中;
注:如果以singleTop模式启动的activity的一个实例已经存在于返回桟中,但是不在桟顶,那么它的行为和standard模式相同,也会创建多个实例;
- singleTask模式:这种模式下,每次启动一个activity时,系统首先会在返回栈中检查是否存在该活动的实例,如果存在,则直接使用该实例,并把这个活动之上的所有活动统统清除;如果没有发现就会创建一个新的活动实例;
- singleInstance模式:总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他activity会自动运行于另一个任务中。当再次启动该activity的实例时,会重新调用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将Intent实例传递到该实例中。和singleTask相同,同一时刻在系统中只会存在一个这样的Activity实例。(singleInstance即单实例)
启动活动的最佳写法
Intent intent =new Intent(FirstActivity.this,SecondActivity.class);
intent.putExtra("param1","data1");
startActivity(intent);
Activity A 启动另一个Activity B,回调如下: Activity A 的onPause() → Activity B的onCreate() → onStart() → onResume() → Activity A的onStop();如果B是透明主题又或则是个DialogActivity,则不会回调A的onStop;
页面跳转
显式跳转
//manifests
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ViewModelTest">
<activity
android:name=".MainActivity"
android:label="Test"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".TestActivity"></activity>
</application>
2.在layout中定义两个页面
3.在主页面定义方法
public class MainActivity extends AppCompatActivity{
private Button jumpBtn;
@Override
protected void onCreate(Bundle savedInstanceState){
super.OnCreate(saveInstanceState);
setContentView(R.layout.activity_main);
//监听器方法
setListeners();
}
private void setListeners(){
OnClick onClick = new OnClick();
jumpBtn.setOnClickListener(onClick);
}
private class OnClick implements View.OnClickListener{
@Override
public void onClick(View v){
//显式跳转1
Intent intent =null;
intent =new Intent(MainActivity.this,TestActivity.class);
startActivity(intent);
//显式2
Intent intent = new Intent();
intent.setClass(MainActivity.this,TestActivity.class);
startActivity(intent);
//显式3
Intent intent = new Intent();
intent.setClassName(MainActivity.this,"com.example.myanimation.TestActivity");
startActivity(intent);
//显式4
Intent intent = new Intent();
intent.setComponent(new ComponentName(MainActivity.this,"com.example.myanimation.TestActivity"));
startActivity(intent);
}
}
}
隐式跳转
//在xml文件定义intent-filter,category应设置为DEFAULT
<activity android:name=".BActivity" android:label="B"
android:exported="true">
<intent-filter>
<action android:name="com.example.myanimation.action.B" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
//A 页面
private class OnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
//隐式1
Intent intent = new Intent();
intent.setAction("com.example.myanimation.action.B");
startActivity(intent);
}
}
页面跳转时数据传递
将A页面数据传递到B页面中
// A页面
private class OnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
//数据传递
Intent intent = new Intent(TestActivity.this, BActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "meizu");
bundle.putInt("number", 88);
intent.putExtras(bundle);
startActivity(intent);
// startActivityForResult(intent,0);
}
}
//B页面
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bactivity);
mTvTitle = findViewById(R.id.tv_title);
mBtnFinish = findViewById(R.id.finishBtn);
//接受上一页面传来的数据
Bundle bundle = getIntent().getExtras();
String name = bundle.getString("name");
int number = bundle.getInt("number");
mTvTitle.setText((name + "," + number));
}
页面回退数据传递
//从B 页面回退到A 页面时
//B 页面
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bactivity);
mTvTitle = findViewById(R.id.tv_title);
mBtnFinish = findViewById(R.id.finishBtn);
//页面回退数据传递
mBtnFinish.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle1 = new Bundle();
bundle1.putString("title","I am back");
intent.putExtras(bundle1);
setResult(Activity.RESULT_OK, intent);
finish();
}
});
}
//A 页面
private class OnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
startActivityForResult(intent,0);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
//接收方法
super.onActivityResult(requestCode, resultCode, data);
Toast.makeText(TestActivity.this, data.getExtras().getString("title"),Toast.LENGTH_LONG).show();
}
安卓开发中布局加载器(LayoutInflater)的使用
LayoutInflater的常见使用场景 (1)在Activity中
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.activity_main, null);
(2)在Fragment中
View view = inflater.inflate(R.layout.fragment_guide_one, container, false);
return view;
(3)在Adapter中
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = LayoutInflater.from(convertView.getContext()).inflate(R.layout.activity_main, parent, false);
return view;
}
(4)特殊情况下,需要使用LayoutInflater,使用如下:
LayoutInflater inflater =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
1
总结:以上使用的场景都是将一个xml布局文件转换成View视图,因此,LayoutInflater的主要作用就是布局加载。
Fragment
- Fragment有自己的生命周期
- Fragment依赖于Activity
- Fragmenti通过getActivity(0可以获取所在的Activity;Activity通过FragmentManager的findFragmentById)或findFragmentByTag)获取Fragment
- Fragment和Activity是多对多的关系
- Fragment回退栈
- Fragment 和Activity之间的通讯
生命周期
- onAttach()。当碎片和活动建立关联的时候调用。
- onCreateview()。为碎片创建视图(加载布局)时调用。
- onActivityCreated()。确保与碎片相关联的活动一定已经创建完毕的时候调用。
- onDestroyView()。当与碎片关联的视图被移除的时候调用。
- onDetach()。当碎片和活动解除关联的时候调用。
fragment生命周期与activity生命周期的一个关键区别就在于,fragment的生命周期方法是由托管activity而不是操作系统调用的。
- 创建Fragment
public class AFragment extends Fragment {
private TextView mTvTitle;
public static AFragment newInstance(String title){
//传递参数
AFragment fragment =new AFragment();
Bundle bundle =new Bundle();
bundle.putString("title",title);
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
/**
*
* 加载xml文件,设置布局文件AFragment
*/
View view =inflater.inflate(R.layout.fragment_a,container,false);
return view;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
/**
* 对事件处理的方法,事件处理有关的内容写在这个方法中
*/
super.onViewCreated(view, savedInstanceState);
mTvTitle=view.findViewById(R.id.tv_title);
//获取参数 getArgument
if(getArguments() !=null){
mTvTitle.setText(getArguments().getString("title"));
}
}
}
- 在主页面实例化Fragment
public class ContainerActivity extends AppCompatActivity {
private AFragment aFragment;
private BFragment bFragment;
private Button mBtnChange;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_container);
mBtnChange=findViewById(R.id.btn_change);
mBtnChange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(bFragment == null){
bFragment = new BFragment();
}
// replace 替换Fragment
getSupportFragmentManager().beginTransaction().replace(R.id.fl_container,bFragment).commitAllowingStateLoss();
}
});
// //实例化AFragment
// aFragment =new AFragment();
// //把AFragment 添加到Activity中,记得调用commit
// getSupportFragmentManager().beginTransaction().add(R.id.fl_container,aFragment).commitAllowingStateLoss();
// 实例化AFragment,传参newInstance
aFragment =AFragment.newInstance("我是参数");
//把AFragment 添加到Activity中,记得调用commit
getSupportFragmentManager().beginTransaction().add(R.id.fl_container,aFragment).commitAllowingStateLoss();
}
}
private void initView() {
HomeFragment homeFragment =new HomeFragment();
FragmentManager fm =getSupportFragmentManager();
FragmentTransaction transaction= fm.beginTransaction();
transaction.add(R.id.main_page_container,homeFragment);
//提交事务
transaction.commit();
}
Fragment回退栈
//containerActivity
public class ContainerActivity extends AppCompatActivity {
private AFragment aFragment;
private BFragment bFragment;
private Button mBtnChange;
private TextView mTvTitle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_container);
mTvTitle=findViewById(R.id.tv_title);
// 实例化AFragment,传参newInstance
aFragment =AFragment.newInstance("我是参数");
//把AFragment 添加到Activity中,记得调用commit,add()第三个参数是Tag,传递值的标签
getSupportFragmentManager().beginTransaction().add(R.id.fl_container,aFragment,"a").commitAllowingStateLoss();
}
//传递数据的方法
public void setData(String text){
mTvTitle.setText(text);
}
}
//AFragment
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
/**
* 对事件处理的方法,事件处理有关的内容写在这个方法中
*/
super.onViewCreated(view, savedInstanceState);
mTvTitle = view.findViewById(R.id.tv_title);
mBtnChange = view.findViewById(R.id.btn_change);
mBtnReset = view.findViewById(R.id.btn_reset);
mBtnChange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bFragment == null) {
bFragment = new BFragment();
}
//获取Tag
Fragment fragment = getFragmentManager().findFragmentByTag("a");
if (fragment != null) {
//addToBackStack 放入栈中,点击返回会回到上一个Fragment
getFragmentManager().beginTransaction().hide(fragment).add(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
} else {
getFragmentManager().beginTransaction().replace(R.id.fl_container, bFragment).addToBackStack(null).commitAllowingStateLoss();
}
}
});
mBtnReset.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTvTitle.setText("new Text");
}
});
//获取参数 getArgument
if (getArguments() != null) {
mTvTitle.setText(getArguments().getString("title"));
}
}
Fragment 和Activity之间的通讯
执行此操作的一个好方法是,在片段内定义一个回调接口,并要求宿主 Activity 实现它。
Android如何优雅地向Fragment传递参数
初始化Fragment实例并setArguments
DiscoverFragment discoverFragment = new DiscoverFragment();
Bundle bundle = new Bundle();
bundle.putString("email", email);
discoverFragment.setArguments(bundle);
在Fragment中拿到Arguments:
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_discover, null);
Bundle bundle = getArguments();
//这里就拿到了之前传递的参数
email = bundle.getString("email");
return view;
}
接口回调
fragment
public class MainFragment extends Fragment{
public FragmentListener mListener;
//MainFragment开放的接口
public static interface FragmentListener{
//跳到h5页面
void toH5Page();
//展示消息
void showMsg(String str);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//对传递进来的Activity进行接口转换
if(activity instance FragmentListener){
mListener = ((FragmentListener)activity);
}
}
...其他处理代码省略
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
msgListener.showMsg("Hello 传递数据给Activity展示");
}
});
}
activity
// MainActivity 实现 MainFragment开放的接口
public class MainActivity extends FragmentActivity implements FragmentListener{
@override
public void toH5Page(){... }
@Override
public void showMsg(String str) {
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
...其他处理代码省略
}
Adapter
BaseAdapter
BaseAdapter是最万能最好用的数据适配器之一,可以给ListView、Spinner、GridView多种控件填充数据。通俗的说,适配器的作用就是在数据和视图之间建立一种桥梁,类似一个转换器,能够将复杂的数据转换成用户可以接受的方式进行呈现。本案例的核心就是继承BaseAdapter类并实现其4个抽象方法: 1.int getCount() 填充的item个数 2.Object getItem(int position) 指定索引对应的item数据项 3.long getItemId(int position) 指定索引对应item的id值 4.View getView(final int position, View convertView, ViewGroup parent) 填充每个item的可视内容并返回 其中最重要最核心的就是getView方法,它负责给每个item填充内容,并且返回视图对象
notifyDataSetChanged
notifyDataSetChanged方法通过一个外部的方法控制如果适配器的内容改变时需要强制调用getView来刷新每个Item的内容,可以实现动态的刷新列表的功能。、
自定义组件
//继承LinearLayout
public class TitleLayout extends LinearLayout{
public TitleLayout(Context context,AttributeSet attrs){
super(context,attrs);
LayoutInflater.from(context).inflate(R.layout.title,this)
}
}
ListView
- 适配器ArrayAdapter
- 调用ListView 的 setAdapter()方法
Recyclerview
RecyclerView封装了viewholder的回收复用,也就是说RecyclerView标准化了ViewHolder,编写Adapter面向的是ViewHolder而不再是View了,复用的逻辑被封装了,写起来更加简单。
基本使用
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this );
//设置布局管理器
recyclerView.setLayoutManager(layoutManager);
//设置为垂直布局,这也是默认的
layoutManager.setOrientation(OrientationHelper. VERTICAL);
//设置Adapter
recyclerView.setAdapter(recycleAdapter);
//设置分隔线
recyclerView.addItemDecoration( new DividerGridItemDecoration(this ));
//设置增加或删除条目的动画
recyclerView.setItemAnimator( new DefaultItemAnimator());
Notification
1.添加依赖
dependencies {
implementation "com.android.support:support-compat:28.0.0"
}
2.创建通知
//
Intent intent =new Intent(MainActivity.this,MoreTypeActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(MainActivity.this,0,intent,0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainActivity.this, CHANNEL_ID)
.setSmallIcon(R.drawable.pict_01)
.setContentTitle("textTitleaaa")
.setContentText("textContentbbb")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true);
NotificationManagerCompat notificationManager = NotificationManagerCompat.from(MainActivity.this);
// notificationId is a unique int for each notification that you must define
int notificationId=1;
notificationManager.notify(notificationId, builder.build());
3.创建通道
private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
3.调用方法
protected void onCreate(Bundle savedInstanceState) {
createNotificationChannel();
}
TabLayout和RecyclerView实现导航滑动、点击效果
<!--activity_main.xml-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".activity.MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_page2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
<!-- <com.google.android.material.tabs.TabLayout-->
<!-- android:id="@+id/tab_layout"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- app:tabMode="scrollable" />-->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabTextColor="@color/black"
app:tabMode="auto"
app:tabSelectedTextColor="@color/lightGreen"/>
</LinearLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private ViewPager2 mViewPager2;
private String[] tabs = {"Flower", "Weather", "Note", "About"};
private ImageView ivFlower, ivWeather, ivNote, ivAbout, ivCurrent;
private TabLayout mTabLayout;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initTabView();
initPager();
}
//绑定TabLayout tab栏
private void initTabView() {
mTabLayout = findViewById(R.id.tab_layout);
mTabLayout.addTab(mTabLayout.newTab().setText(tabs[0]));
mTabLayout.addTab(mTabLayout.newTab().setText(tabs[1]));
mTabLayout.addTab(mTabLayout.newTab().setText(tabs[2]));
mTabLayout.addTab(mTabLayout.newTab().setText(tabs[3]));
Log.d(TAG, "initTabView: " + mTabLayout);
}
private void initPager() {
//实例化fragment
mViewPager2 = findViewById(R.id.view_page2);
ArrayList<Fragment> fragments = new ArrayList<>();
fragments.add(FlowerFragment.newInstance(""));
fragments.add(new WeatherFragment());
fragments.add(new NoteFragment());
fragments.add(AboutFragment.newInstance("about"));
FragmentStateAdapter pagerAdapter = new FragmentStateAdapter(getSupportFragmentManager(), getLifecycle(), fragments, Arrays.asList(tabs));
mViewPager2.setAdapter(pagerAdapter);
//viewpager滑动监听
mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
//页面改变绑定
changeTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
}
});
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
Log.i(TAG, "onTabSelected:" + tab.getText());
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
//viewpaper和tabLayout 绑定
new TabLayoutMediator(mTabLayout, mViewPager2, true, new TabLayoutMediator.TabConfigurationStrategy() {
@Override
public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
//这里需要根据position修改tab的样式和文字等
tab.setText(tabs[position]);
}
}).attach();
}
}
//adapter
public class FragmentStateAdapter extends androidx.viewpager2.adapter.FragmentStateAdapter {
List<Fragment> fragmentList = new ArrayList<>();
List<String> strs = new ArrayList<>();
public FragmentStateAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments,List<String> titles) {
super(fragmentManager, lifecycle);
fragmentList = fragments;
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
}
监听事件处理机制
三要素
- Event Source(事件源)
- Event(事件)
- Event Listener(事件监听器)
事件监听器
- onClick
- onLongClick
- onFocusChange
- onKey
- onTouch
- onCreateContextMenu
示例:
点击事件
recyclerView = findViewById(R.id.rv);
recyclerView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG,"OnCLick---")
}
});
触摸事件
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
});
返回一个布尔值,指示您是否已处理完事件,以及是否应将其继续传递下去。换言之,返回 true 表示您已处理事件且事件应就此停止;如果您尚未处理事件且/或事件应继续传递给其他任何按键监听器,则返回 false。
实现监听事件的方法:
- 通过内部类实现
- 通过匿名内部类实现
- 通过事件源所在类实现
- 通过外部类实现
- 布局文件中onClick属性(针对点击事件)
给同一事件源添加多个同类型监听器
只会执行最后一个设置的监听器
xml设置的onClick是最先执行的事件
动画
public class MainActivity extends AppCompatActivity {
private TextView tvTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvTest = findViewById(R.id.tv);
//沿y轴向下平移500dp
tvTest.animate().translationYBy(500).setDuration(2000).start();
/**
* 属性动画 ValueAnimator ObjectAnimator
* 对象跟踪动画的时间
*
*/
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.setDuration(2000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.d("aaa",animation.getAnimatedValue()+"");
Log.d("bbb",animation.getAnimatedFraction()+"");
}
});
valueAnimator.start();
ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(tvTest,"translationY",0,500,200,300);
objectAnimator.setDuration(2000);
objectAnimator.start();
}
广播 Broadcast Receiver
Android 8.0后使用动态注册
动态注册
public class MainActivity extends AppCompatActivity {
private static final String TAG = "myTag+";
private TextView mBatteryLevelText;
private BatteryLevelReceiver mBatteryLevelReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
registerBatteryReceiver();
}
private void initView() {
mBatteryLevelText = findViewById(R.id.battery_level);
}
private void registerBatteryReceiver() {
//2.我们要收听的频道是:电量变化
IntentFilter intentFilter = new IntentFilter();
//3.设置频道
intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
//4.
mBatteryLevelReceiver = new BatteryLevelReceiver();
//5.注册广播,动态注册,跟着生命周期创建和销毁
this.registerReceiver(mBatteryLevelReceiver, intentFilter);
}
/**
* 1.创建一个广播接收者
*/
public class BatteryLevelReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
Log.d(TAG, "onReceive: action is ==" + action);
int currentLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
Log.d(TAG, "当前电量:" + currentLevel);
if (mBatteryLevelText != null) {
mBatteryLevelText.setText("当前电量:" + currentLevel);
}
int maxLevel = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
//拿到当前电量后,再除以最大值
float percent = currentLevel * 1.0f / maxLevel * 100;
Log.d(TAG, "当前电量百分比是:" + percent + "%");
} else if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
Log.d(TAG, "usb连接上了。。。。。 ");
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
Log.d(TAG, "usb断开了。。。。。 ");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消广播注册
if(mBatteryLevelReceiver !=null){
this.unregisterReceiver(mBatteryLevelReceiver);
}
}
}
静态注册
//manifest
//添加权限
<uses-permission android:name="android.permission.BATTERY_STATS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BroadcastAppp"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//添加receiver
<receiver android:name=".BootCompleteReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
//BootComlpeteReceiver
/**
* 1.创建一个类,继承BroadcastReceiver
* 2.在Manifest创建一个receiver
* 3.创建intent-filter
*/
public class BootCompleteReceiver extends BroadcastReceiver {
private static final String TAG = "BootCompleteReceiver";
@Override
public void onReceive(Context context, Intent intent) {
String action =intent.getAction();
Log.d(TAG, "action is =="+action);
Log.d(TAG, "开机完成....");
Toast.makeText(context,"收到开机的广播...",Toast.LENGTH_SHORT).show();
}
}
有序广播
- 有序
- 可以终止往下传达
- 可以修改广播的内容
public class SendOrderBroadcastActivity extends Activity {
private Object SendOrderBroadcastActivity;
private HighLevelReceiver mHighLevelReceiver;
private DefaultLevelReceiver mDefaultLevelReceiver;
private LowLevelReceiver mLowLevelReceiver;
private static final String DYNAMICACTION = "com.example.broadcastappp.ORDER_BROADCAST";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send_order_broadcast);
SendOrderBroadcastReceiver();
}
//点击按钮发送广播
public void sendOrderBroadcast(View view) {
Intent intent = new Intent();
//设置广播信息的action名字
intent.setAction(DYNAMICACTION);
//设置广播信息的内容为“这是一条自定义广播消息”
intent.putExtra("content", "1231322");
//发送有序广播,第一个参数是intent,第二个参数表示收到广播的权限信息,null表示可以被所有广播接受者接受
sendOrderedBroadcast(intent, null);
}
private void SendOrderBroadcastReceiver() {
//广播过滤器的action为MyBroadcast,与发送的广播信息的action要一致
IntentFilter intentFilter = new IntentFilter(DYNAMICACTION);
//priority表示等级,值是 -1000~1000,默认是0
intentFilter.setPriority(1000);
mHighLevelReceiver = new HighLevelReceiver();
////注册广播接收者,第一个参数为广播接收者,第二个参数为实例化的过滤器
registerReceiver(mHighLevelReceiver, intentFilter);
IntentFilter intentFilter2 = new IntentFilter(DYNAMICACTION);
IntentFilter intentFilter3 = new IntentFilter(DYNAMICACTION);
intentFilter.addAction(DYNAMICACTION);
intentFilter2.setPriority(0);
intentFilter3.setPriority(-100);
mDefaultLevelReceiver = new DefaultLevelReceiver();
mLowLevelReceiver = new LowLevelReceiver();
registerReceiver(mDefaultLevelReceiver, intentFilter2);
registerReceiver(mLowLevelReceiver, intentFilter3);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mHighLevelReceiver != null) {
unregisterReceiver(mHighLevelReceiver);
}
}
}
参考链接:安卓学习笔记六(广播接收者创建、静态注册、动态注册、发送自定义广播信息)动态注册是在清单文件中实现的
终止广播往下传递
public class HighLevelReceiver extends BroadcastReceiver {
private static final String TAG = "HighLevelReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "action: "+intent.getAction());
//终止往下传达
abortBroadcast();
}
}
如何使用receiver
我们无法使用任何异步API或登记任何监听器,因为onReceive(Context,Intent)方法刚运行完,receiver就不存在了。
ContentProvider 内容提供者
Android 的数据存储方式总共有五种,分别是:Shared Preferences、网络存储、文件存储、外储存储、SQLite。但一般这些存储都只是在单独的一个应用程序之中达到一个数据的共享,有时候我们需要操作其他应用程序的一些数据,就会用到 ContentProvider。而且 Android 为常见的一些数据提供了默认的 ContentProvider(包括音频、视频、图片和通讯录等)。
ContentResolver
对于每一个应用程序,如果想要访问内容提供器中的共享数据,就要借助ContentResolver 类
方法名称 | 作用 |
---|---|
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) | 用于对 Uri 指定的 ContentProvider 进行查询。 |
insert(Uri uri, ContentValues values) | 用于添加数据到 Uri 指定的 ContentProvider 中。 |
delete(Uri uri, String selection, String[] selectionArgs) | 用于从 Uri 指定的 ContentProvider 中删除数据。 |
update(Uri uri, ContentValues values, String selection, String[] selectionArgs) | 用于更新 Uri 指定的 ContentProvider 中的数据。 |
URI
// 规则
[scheme:][//host:port][path][?query]
// 示例
content://com.wang.provider.myprovider/tablename/id:
Service 服务
1、前台进程:可以理解为是最顶部的,直接跟用户交互的。比如说我们操作的Activity界面 2、可见进程:可以见的,但是不操作的,比如说我们在一个Activity的顶部弹出一个Dialog,这个Dialog就是前台进程,但是这个Activity则是可见进程。 3、服务进程:服务可以理解为是忙碌的后台进程,虽然是在后台,但是它很忙碌。 4、后台进程:后台进程就是退隐到后台,不做事的进程。 5、空进程:空进程是不做事的,没有任何东西在上面跑着,仅作缓存作用。
使用<service>
标签,其实跟前面的activity,广播接收者receiver一样生命。
通过Context.startService()来开启服务,通过Context.stop()来停止服务。当然啦,还有一种启动形式就是通过Context.bindService()的方法。
startService(intent);
intent.setClass(MainActivity.this, FirstService.class); stopService(intent);
生命周期
public class FirstService extends Service{
private static final String TAG = "FirstService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate...");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand...");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy...");
super.onDestroy();
}
}
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button mBtnStart;
private Button mBtnStop;
private Button mBtnBind;
private Button mBtnUnBind;
private Button mCallService;
private Boolean mIsServiceBinded;
private FirstService.InnerBinder mRemoteBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnStart = findViewById(R.id.StartBtn);
mBtnStop = findViewById(R.id.StopBtn);
mBtnBind = findViewById(R.id.BindServe);
mBtnUnBind = findViewById(R.id.unBindBtn);
mCallService = findViewById(R.id.callService);
setListener();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
}
private void setListener() {
OnClick OnClick = new OnClick();
mBtnStart.setOnClickListener(OnClick);
mBtnStop.setOnClickListener(OnClick);
mBtnBind.setOnClickListener(OnClick);
mBtnUnBind.setOnClickListener(OnClick);
mCallService.setOnClickListener(OnClick);
}
private class OnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent();
switch (v.getId()) {
case R.id.StartBtn:
Log.d(TAG, "start..........");
intent.setClass(MainActivity.this, FirstService.class);
startService(intent);
break;
case R.id.StopBtn:
Log.d(TAG, "stop..........");
intent.setClass(MainActivity.this, FirstService.class);
stopService(intent);
break;
case R.id.BindServe:
Log.d(TAG, "BindServe..........");
intent.setClass(MainActivity.this, FirstService.class);
mIsServiceBinded = bindService(intent, mConnection, BIND_AUTO_CREATE);
break;
case R.id.unBindBtn:
Log.d(TAG, "unBindBtn..........");
intent.setClass(MainActivity.this, FirstService.class);
stopService(intent);
break;
case R.id.callService:
Log.d(TAG, "callService: ");
mRemoteBinder.callServiceInnerMethod();
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected: ");
mRemoteBinder = (FirstService.InnerBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected: ");
mRemoteBinder=null;
}
};
}
}
//manifest 注册组件
<service android:name=".FirstService"/>
bindService
1、绑定服务在系统设置里是没有显进服务正在跑着的;
2、如果onBind方法返回的是null,那么onServiceConnected方法不会被调用;
3、绑定服务的生命周期跟Activity是不求同时生,但求同时死,Activity没了,服务也要解绑;
4、服务在解除绑定以后会停止运行,执行unBind方法—>onDestroy方法;
5、绑定服务开启的服务,只可以解绑一次,多次解绑会抛异常;
6、绑定的connection要跟解绑的connection要对应着,否则没法解绑。
startService和bindService的区别,优点和缺点:
1、startService这个方法来启动服务的话,是长期运行的,只有stopService才会停止服务。而bindService来启动服务,不用的时候,需要调用unBindService,否则会导致context泄漏,所以bindService不是长期运行的。当context销毁的时候,则会停止服务运行。
2、startService来启动服务可以长期运行,但是不可以通讯,而bindService的方式来启动服务则可以通讯,两者都有优缺点,所以我们就有了混合起来使用的方法。
AIDL
AIDL:android interface definition language 安卓接口定义语言
IPC:inter process communication 进程间通讯
网络请求
java Api请求
public void loadJson(View view) {
new Thread(new Runnable() {
@Override
public void run() {
try {
URL url = new URL("http://172.16.35.173:9102/get/text");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(10000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9");
connection.setRequestProperty("Accept-Encoding", "gzip,deflate");
connection.setRequestProperty("Accept", "*/*");
connection.connect();
//result code
int responseCode = connection.getResponseCode();
if (responseCode == 200) {
Map<String, List<String>> headerField = connection.getHeaderFields();
Set<Map.Entry<String, List<String>>> entries = headerField.entrySet();
for (Map.Entry<String, List<String>> entry : entries) {
Log.d(TAG, "loadJson: " + entry.getKey() + "==" + entry.getValue());
}
Object content = connection.getContent();
Log.d(TAG, "loadJson: content==" + content);
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String json = bufferedReader.readLine();
// Log.d(TAG, "run: " + line);
Gson gson=new Gson();
GetTextItem getTextItem=gson.fromJson(json,GetTextItem.class);
updateUI(getTextItem);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
okHttp请求
implementation 'com.growingio.android:okhttp3:3.3.6'
public void okGetRequest(View view) {
//Create a client
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
//create request content
Request request = new Request.Builder()
.get()
.url(BASE_URL + "/get/text")
.build();
//use client to create request task
Call task = okHttpClient.newCall(request);
//async request
task.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: " + e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
int code = response.code();
Log.d(TAG, "onResponse: code-->" + code);
ResponseBody body = response.body();
Log.d(TAG, "onResponse: body" + body.string());
}
});
}
public void okPostRequest(View view) {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10000, TimeUnit.MILLISECONDS)
.build();
CommentItem commentItem = new CommentItem("234134123", "commtent123");
Gson gson = new Gson();
String jsonStr = gson.toJson(commentItem);
MediaType mediaType = MediaType.parse("application/json");
RequestBody requestBody = RequestBody.create(mediaType, jsonStr);
Request request = new Request.Builder()
.post(requestBody)
.url(BASE_URL + "/post/comment")
.build();
Call task = client.newCall(request);
task.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
Log.d(TAG, "onFailure: -->" + e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
int code = response.code();
Log.d(TAG, "onResponse: " + code);
if (code == HttpURLConnection.HTTP_OK) {
ResponseBody body = response.body();
if (body != null) {
Log.d(TAG, "onResponse: body result" + body.string());
}
}
}
});
}
Retrofit请求
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
- 定义接口
public interface API {
@GET("get/text")
Call<GetTextItem2> getJson();
@GET("get/param")
Call<GetWithParamsResult> getWithParams(@Query("keyword") String keyword,@Query("page") int page,@Query("order") String order);
}
- 封装方法
public class RetrofitManager {
private static Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://172.16.35.173:9102")
.addConverterFactory(GsonConverterFactory.create())
.build();
public static Retrofit getRetrofit() {
return retrofit;
}
}
- 调用方法
public void retrofitHttp(View view) {
Retrofit retrofit = new Retrofit
.Builder()
//converter-gson
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://172.16.35.173:9102")
.build();
API api = retrofit.create(API.class);
Call<GetTextItem2> task = api.getJson();
task.enqueue(new Callback<GetTextItem2>() {
@Override
public void onResponse(Call<GetTextItem2> call, Response<GetTextItem2> resnse) {
Log.d(TAG, "onResponse: " + resnse.code());
if (resnse.code() == HttpURLConnection.HTTP_OK) {
GetTextItem2 result = resnse.body();
updateList(result);
}
}
@Override
public void onFailure(Call<GetTextItem2> call, Throwable t) {
Log.d(TAG, "onFailure: " + t.toString());
}
});
}
数据库
SQLiteDatabase 增删改查
创建SQLiteDatabase,创建表 定义数据结构DataBean 定义增删改查逻辑
异步任务和多线程
- 主线程不能执行网络请求/文件读写等耗时操作
- 子线程不能执行UI刷新
Handler 异步通信系统
网络请求
public class NetUtil {
private static BufferedReader reader;
public static String doGet() {
String result = "";
BufferedReader reader = null;
String bookJSONString = null;
try {
//1.建立连接
HttpURLConnection httpURLConnection = null;
String url = "https://www.baidu.com";
URL requestUrl = new URL(url);
httpURLConnection = (HttpURLConnection) requestUrl.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.setConnectTimeout(5000);
httpURLConnection.connect();
//2.获取二进制
InputStream inputStream=httpURLConnection.getInputStream();
//3.将二进制流包装
reader = new BufferedReader(new InputStreamReader(inputStream));
//4.从BufferedReader中读取string字符串,拼接字符串
String line;
StringBuilder builder=new StringBuilder();
while ((line=reader.readLine()) !=null){
builder.append(line);
builder.append("\n");
}
if(builder.length()==0){
return null;
}
result=builder.toString();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
设置允许访问网络
<uses-permission android:name="android.permission.INTERNET" />
Gson使用
引用包
dependencies {
implementation 'com.google.code.gson:gson:2.9.0'
}
Gson gson=new Gson();
WeatherBean weatherBean = gson.fromJson("jsonStr", WeatherBean.class);
xUtils3
implementation 'org.xutils:xutils:3.8.12'
xUtils 包含了orm, http(s), image, view注解, 但依然很轻量级(251K), 并且特性强大, 方便扩展。
问题记录
无法解析AppCompatActivity上的符号ViewModelProviders
我的构建中没有两个依赖项,因此出现了问题。
implementation "android.arch.lifecycle:extensions:1.1.0"
implementation "android.arch.lifecycle:viewmodel:1.1.0"
空指针java.lang.NullPointerException
import android.support.v4.app.ActivityCompat;编译错误
如果有如下错误: import android.support.v4.app.ActivityCompat; import android.support.v7.app.AppCompatActivity;
可以改为: import androidx.core.app.ActivityCompat; import androidx.appcompat.app.AppCompatActivity;
去Google android研发网站查找到了关于Android X的解释:
AndroidX 对原始 Android 支持库进行了重大改进,后者现在已不再维护。 androidx 软件包完全取代了支持库,不仅提供同等的功能,而且提供了新的库。 此外,AndroidX 还包括以下功能:
- AndroidX 中的所有软件包都使用一致的命名空间,以字符串 androidx 开头。支持库软件包已映射到对应的 androidx.* 软件包。有关所有旧类到新类以及旧构建工件到新构建工件的完整映射,请参阅软件包重构页面。
- 与支持库不同,androidx 软件包会单独维护和更新。从版本 1.0.0 开始,androidx 软件包使用严格的语义版本控制。您可以单独更新项目中的各个 AndroidX 库。
- 版本 28.0.0 是支持库的最后一个版本。我们将不再发布 android.support 库版本。所有新功能都将在 androidx 命名空间中开发。
Android中Activity和AppcompatActivity的区别
AppCompatActivity默认带标题,但Activity不带
去掉AppcompaActivity的标题栏方法:
if (getSupportActionBar()!=null){ getSupportActionBar().hide(); }
修改themes.xml,改成NoActionBar
<style name="Theme.Forecast" parent="Theme.MaterialComponents.DayNight.NoActionBar">
</style>
category android:name="android.intent.category.DEFAULT"
作用:隐式跳转时,匹配activity 类别。通过指定3个参数:action,category,data,然后让系统去寻找能够匹配得上这三个参数的Acativity,如果有多个符合条件的Activity,就会让用户选择其中一个打开。
参考链接:android细节之android.intent.category.DEFAULT的使用
BottomNavigationView menu 只显示一项
解决方案: 添加 app:labelVisibilityMode="labeled"
<com.google.android.material.bottomnavigation.BottomNavigationView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:labelVisibilityMode="labeled"
app:menu="@menu/my_navigation_items" />
LinnearLayout item居中布局
在LinnerLayout中设置
android:layout_gravity="center" android:gravity="center"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/button"
/>
<TextView
android:id="@+id/tv_name"
android:gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="武装"
android:textSize="25sp"
android:textColor="#fff"
android:layout_marginLeft="30dp"
/>
</RelativeLayout>
</LinearLayout>
GSON 2.9 JsonParser方法失效
2.8.0的写法
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.0'
//Json的解析类对象
JsonParser parser = new JsonParser();
//将JSON的String 转成一个JsonArray对象
JsonArray jsonArray = parser.parse(JsonData).getAsJsonArray();
Gson gson = new Gson();
ArrayList<FlowerBean> userBeanList = new ArrayList<>();
//加强for循环遍历JsonArray
for (JsonElement user : jsonArray) {
//使用GSON,直接转成Bean对象
FlowerBean userBean = gson.fromJson(user, FlowerBean.class);
userBeanList.add(userBean);
}
Git异常 #Unable to negotiate with xx.xx.xx.xx port 29418: no matching key exchange method found.
cd ~/.ssh
在C盘 “用户/当前用户名/.ssh/” 目录下新建一个 config 文件,无扩展名。然后使用写字板等工具打开,输入并保存以下内容:
Host * KexAlgorithms +diffie-hellman-group1-sha1
Android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法
android:usesCleartextTraffic="true"
Retrofit:response.body().string()只能调用一次
response.body().string()只能调用一次,第二次为空。
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> resnse) {
Log.d(TAG, "onResponse: " + resnse.code());
if (resnse.code() == HttpURLConnection.HTTP_OK) {
try {
// Log.d(TAG, "onResponse: json--->" + resnse.body().string());
String result=resnse.body().string();
Log.d(TAG, "onResponse: result"+result);
Gson gson = new Gson();
GetTextItem2 getTextItem2 =gson.fromJson(result,GetTextItem2.class);
Log.d(TAG, "onResponse:jsonResult66 "+getTextItem2);
updateList(getTextItem2);
} catch (IOException e) {
e.printStackTrace();
}
}
}
快捷键
快捷键 | 功能 |
---|---|
Ctrl + alt + L; | 格式化代码 |
Ctrl + R | 查找替换 |
Alt + Insert | 自动生成generate |
Alt + Enter | 类实例 |
Ctrl + F12 | 查看类的方法名 |
Ctrl + Shift +↑↓ | 上下移动代码 |
ctrl + alt + T | 快捷代码块(try catch...) |
alt+ctrl+左/右按键 | 回到上一次代码浏览位置 |
Ctrl + Alt +F | 成员变量 |
Ctrl + Shift + U | 转换大小写 |
Ctrl + P | 显示参数 |
Gradle
Gradle 是一个打包工具
依赖项配置
配置 | 说明 |
---|---|
implementation | Gradle 会将依赖项添加到编译类路径,并将依赖项打包到编译输出。不过,当模块配置 implementation 依赖项时,其他模块只有在运行 时才能使用该依赖项。 |
api | Gradle 会将依赖项添加到编译类路径和编译输出。当一个模块包含api 依赖项时,会让 Gradle 了解该模块要以传递方式将该依赖项导出到其他模块,以便这些模块在运行时和编译时都可以使用该依赖项。 |
compileOnly | Gradle 只会将依赖项添加到编译类路径(也就是说,不会将其添加到编译输出)。 |
runtimeOnly | Gradle 只会将依赖项添加到编译输出,以便在运行时使用。也就是说,不会将其添加到编译类路径。 |
annotationProcessor | 要添加对作为注解处理器的库的依赖关系,必须使用annotationProcessor 配置将其添加到注解处理器类路径。 |
\