最强安卓笔记

news/2024/12/29 16:40:38/

经过一年的安卓学习,最后要结束了,整理一下,学过的资料包含《疯狂安卓讲义》,《安卓群英传》,《安卓高级开发》,《Android多媒体开发高级编程》和一些平常搜集到的知识点

编写这个博客主要是怀念安卓开发,知识量巨多,观看需谨慎,但若所有的知识点你都很清楚,那你安卓开发也是挺棒的

若你是搜索问题,搜到了这个页面建议Ctrl+F搜索一下,说不定真的有方案解决你的问题

animated-vector

<?xml version="1.0" encoding="utf-8"?>
<!--
android:drawable  引用绘图资源  vector
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" android:drawable="@drawable/ic_content_cut_black_24dp">
<!--
android:animation  引用动画  objectAnimator
android:name引用分段  path name
-->
<targetandroid:animation="@animator/my"android:name="test"/>
</animated-vector>

添加水波纹效果

android:clickable="true"//确保可以点击
android:background="?attr/selectableItemBackground"//添加效果

绘制顶点

注意关闭硬件加速 纹理放到画笔中

float[] vs={0,300,300,300,300,0};
float[] ts={0,300,300,300,300,0};
//必须点与 上面的数量相同
int[] colors={Color.RED,Color.GREEN,Color.BLUE,Color.RED,Color.GREEN,Color.BLUE};
canvas.drawVertices(Canvas.VertexMode.TRIANGLES,6,vs,0,ts,0,colors,0,null,0,0,paint);

常用快捷键

Ctrl+E 显示最近内容

Ctrl+Shift+上下箭头 交换两行

Ctrl+B 鼠标位于变量 快速搜索变量使用

Ctrl+P 查看方法参数

Ctrl+[+/-] 展开/折叠代码

右键查找使用(查看哪里使用了)

image-20210313204013825

右键断点设置条件断点

image-20210313204259933

断点页面可以添加各种断点 例如异常断点发生异常会停止

image-20210313205053053

按住alt 框选代码

image-20210313205254979

对于Xml同样可以抽取样式

image-20210313210256211

Shift+A 快速搜索

image-20210313210410240

Ctrl+H 查看类结构

image-20210313210858992

可以在tool中找到以前的监视工具

image-20210313212938878

使用vitamio做万能播放器

先引入mdule(推荐使用以前成功过的)

然后参照自己的项目修改vitamio的gradle信息

image-20210524102447638

添加对应权限

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

使用前必须初始化 Vitamio.isInitialized(this); 其他使用与VideoView或MediaPlayer一样(其提供了与默认安卓一致的API)

组合组件的使用

public class Sidebar extends LinearLayout {// 注意继承的类  必须是布局的最外布局private RecyclerView show;public Sidebar(Context context, @Nullable AttributeSet attrs) {super(context, attrs);LayoutInflater.from(context).inflate(R.layout.view_sidebar,this,true);//加载布局  这里需要附加(注意参数)show=findViewById(R.id.rv_show);//可以正常获取组件使用}
}

对于复杂,重复布局可以使用include解耦

RecyclerView可以借助viewType属性显示不同页面

@Override
public int getItemViewType(int position) {//需要在Adapter中根据不同位置设置不同类型(重写该方法)return Math.min(position,3);
}

DrawerLayout(侧滑)使用

对内部组件设置android:layout_gravity="<gravity>"会给对于组件添加滑动功能方向取决于设置的gravity,没有设置的会做为主内容,也可以使用代码控制开闭,添加监听

BottomNavigationView使用

<com.google.android.material.bottomnavigation.BottomNavigationViewandroid:layout_gravity="bottom"//设置导航位置app:itemIconTint="@color/bnv"//设置图标颜色   这里颜色使用的是选择器  否则选择没有颜色变换app:itemTextColor="@color/bnv"//设置文本颜色app:labelVisibilityMode="labeled"//显示所有标题android:background="?android:attr/windowBackground"app:menu="@menu/navigation"//指定显示的菜单资源android:id="@+id/bnv_show"android:layout_width="match_parent"android:layout_height="wrap_content" />

Fragment

image-20210524112728318

注意getView()用于获取对应的View必须在onCreateView()调用后才能获取对象View

使用fragmentManager.beginTransaction().replace(R.id.fl_show,fragment).commitNowAllowingStateLoss();会导致上面的整个生命周期都走一遍,会销毁View,但是数据不会销毁

官方推荐使用setArguments与getArguments传递信息(实际就是其内部的一个变量值)

小建议

可以抽取公用元素进行复用(封装)

若嵌套内部组件没有正常工作可以尝试使用容器包裹一层

善用RecyclerView可以显示多个不同子元素的特点

对于分离的视图组件推荐传递Context能够获取更多对象

onDraw注意要触发重绘事件才绘制 postInvalidate(非本线程) invalidate(本线程触发)

merge标签用于包裹布局文件,生成布局文件后会自动消失

修正GridView嵌套使用只显示一行

public class CorrectionGridView extends GridView {public CorrectionGridView(Context context, AttributeSet attrs) {super(context, attrs);}public CorrectionGridView(Context context) {super(context);}public CorrectionGridView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}@Override//  需要继承该类  重写 测量方法public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);super.onMeasure(widthMeasureSpec, expandSpec);}
}

对于抽取的ViewHolder可以使用工厂模式(其对于的View一般固定,只是修改样式属性)

public static GridViewHolder getInstance(Context context){// 使用静态方法进行构建View view = LayoutInflater.from(context).inflate(R.layout.view_grid, null);//自处理固定逻辑return new GridViewHolder(view,context);
}
private GridViewHolder(View view,Context context) {//隐藏构造方法super(view);gridView=view.findViewById(R.id.gv_show);this.context=context;
}

居中对齐

image-20210525145301623

可以让窄的上下都与宽的对齐,再设置android:gravity=“center”,若要调整背景可以调整layout_margin

黄油刀注意

若View与注解标明对象是同一个对象可以直接使用ButterKnife.bind(this);

若不是同一个需要指定注解使用对象与视图对象ButterKnife.bind(this,itemView);

@OnClick({R.id.btn_unknown,R.id.btn_3day,R.id.btn_5day,R.id.btn_delete})//可以指定多个事件源,通过id判断

使对象均匀分布

image-20210525164655884

使用ConstraintLayout(弹簧布局)左右互相连接

LinearLayout嵌套match_parent失效

推荐使用RelativeLayout解决失效问题

多个请求调用判断是那个响应回调

根据返回值结果判断,例如使用APIweb项目,可以添加tag标记,从返回结果中获取tag标记

安卓真机连接到电脑服务器

注意ip地址是ipconfig的 无线局域网适配器 WLAN 的ipv4地址

对于API27及以上需要设置允许不加密内容传输(否则必须使用https)

在xml文件夹下创建 xxx.xml配置文件

<?xml version="1.0" encoding="utf-8"?>
<network-security-config><base-config cleartextTrafficPermitted="true" />
</network-security-config>

在AndroidManifest.xml引用该配置文件

<applicationandroid:networkSecurityConfig="@xml/network_security_config"//你的配置文件名android:usesCleartextTraffic="true"

设置状态栏颜色

getWindow().setStatusBarColor(0xfffb7299);

设置屏幕旋转方向

setRequestedOrientation(portrait?ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

监听Activity配置改变自己处理改变防止重写创建

默认Activity配置改变会重新创建(例如屏幕旋转)

可以在配置文件中指明自己处理的改变 android:configChanges=“orientation|screenSize”

@Override//在该回调中处理配置改变
public void onConfigurationChanged(@NonNull Configuration newConfig) {super.onConfigurationChanged(newConfig);//必须调用父类方法
}

设置指定EditText获取焦点并弹出输入键盘

input.setFocusable(true);
input.setFocusableInTouchMode(true);
input.requestFocus();
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);

使用对应的LayoutParams参数用于强制调整布局

RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(width, height);
params.leftMargin= (int) (-x/metrics.scaledDensity);
show.setLayoutParams(params);

OkHttpUtils可以通过设置请求Id,区分不同的请求

OkHttpUtils.get().url("").id(2233).build().execute(new StringCallback() {@Override//  请求时可以设置idpublic void onError(Call call, Exception e, int id) {}//发生错误  或  响应时  会返回 id可以根据id判断请求 谁发送的@Overridepublic void onResponse(String response, int id) {}
});
request.addParams()//注意底层使用的map存储  因此无法传递数组参数(会互相覆盖)

View绘图

推荐使用getWidth()与getHeight()获取宽高,且一般在绘图时才能获取到值

WebView直接显示指定html内容

WebView.loadData(<html内容>,"text/html",  "utf-8");

BitmapFactory.Options

设置图片解码设置,inXXX对输入进行设置,outXXX对输出进行设置,inJustDecodeBounds设置只获取图片信息并不加载

通过ExifInterface操作指定图片属性

ExifInterface anInterface = new ExifInterface("/sdcard/赵信.jpg");

可以设置获取属性 anInterface.getThumbnailBitmap();//获取图片缩略图

选择图片功能

选择意图:startActivityForResult(new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI),2233);

在回调中获取图片的URI并解析为Bitmap

Uri data1 = data.getData();
Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(data1));//推荐要关闭流

在图片上绘制(注意直接加载的图片无法修改)

Canvas canvas = new Canvas(bitmap);// 使用bitmap构建Canvas进行绘图,会直接作用到原bitmap

Matrix操作

setXXX()会覆盖矩阵数据 postXXX()矩阵后乘 preXXX()矩阵前乘

设置绘图混合模式

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));// 设置绘图混合默认

绘图使用自定义字体

Typeface asset = Typeface.createFromAsset(getAssets(), "en_gd.ttf");
paint.setTypeface(asset);

音乐Service中更新播放UI

private static Test4Activity activity;
//全局设置  创建设置为自己  销毁设置为null(垃圾回收)  调整ui时判断Test4Activity是否为null再调用
public static void setActivity(Test4Activity activity) {MusicService.activity = activity;//相当于  若自己存在就注册自己,不存在取消注册  类似广播
}

MediaPlayer设置缓冲监听

mediaPlayer.setOnBufferingUpdateListener((mp, percent) -> {//  百分比  数据show.setSecondaryProgress(mp.getDuration()*percent/100);
});

使用意图简单录音

startActivityForResult(new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION),2233);

Uri uri = data.getData();
player.setDataSource(this,uri);// 类似 图片 使用  意图录音

获取视频封面

URI:MediaStore.Video.Thumbnails.EXTERNAL_CONTENT_URI

MediaStore.Video.Thumbnails.DATA路径字段(byte[]与音乐等路径类似)

使用遮罩过滤

paint.setMaskFilter(new BlurMaskFilter(5, BlurMaskFilter.Blur.INNER));//设置过滤
new BlurMaskFilter(5, BlurMaskFilter.Blur.INNER);//模糊过滤		模糊半径	模糊类型
new EmbossMaskFilter(new float[]{1,1,1},5,0.5f,5);//浮雕效果	光源方向	环境光强度	反射等级	模糊程度

使用颜色过滤

paint.setColorFilter(new ColorMatrixColorFilter(matrix));// 设置过滤
ColorMatrix matrix = new ColorMatrix(new float[]{// 颜色矩阵过滤2,0,0,0,0,    // 控制红色  红色=oldRed*2+oldGreen*0+oldBlue*0+oldAlpha*0+00,1,0,0,0,  //控制  绿色0,0,1,0,0,//控制 蓝色0,0,0,1,0// 控制透明度
});// 设置到画笔上
new LightingColorFilter(1,23);//所有像素先乘前面的 再加后面的 会被限制到  0-255
new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.ADD);//指定与颜色的混合模式绘制

动画Drawable

image-20210601103211646

创建的动画Drawable都可以通过image.setImageLevel(level);(0-10000);设置动画进度

注意若依赖下载失败查看是否自己开了代理

使用IjkMediaPlayer播放本地flv

添加依赖(其他省略了)

android {defaultConfig {ndk {abiFilters "armeabi-v7a", "arm64-v8a", "x86", "armeabi"}}sourceSets {main {jniLibs.srcDirs = ['libs']}}
}
dependencies {implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'api 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.4'
}

使用类似MediaPlayer(需要结合SurfaceView)

操控剪切板

ClipboardManager manager= getSystemService(ClipboardManager.class);//获取剪切板服务
if(manager.hasPrimaryClip()){// 判断是否有文本ClipData data = manager.getPrimaryClip();//获取文本
}else {//有获取内容  没有设置内容manager.setPrimaryClip(ClipData.newPlainText("text","HelloWorld"));//设置文本
}

使用Notification构建前台

Notification build = new Notification.Builder(this).setContentTitle("标题").setContentText("内容").setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.icon)).setVisibility(NotificationCompat.VISIBILITY_PUBLIC)//设置显示等级(锁屏也显示).setSmallIcon(R.drawable.icon2).setColor(0xfb7299).addAction(new Notification.Action(R.drawable.icon, "Ok", null))//使用广播 控制.addAction(new Notification.Action(R.drawable.icon2, "Ok", null)).addAction(new Notification.Action(R.drawable.icon3, "Ok", null)).setStyle(new Notification.MediaStyle())//设置媒体播放样式.build();
NotificationManager manager= getSystemService(NotificationManager.class);
manager.notify(2233,build);//显示前台

image-20210601143707580

桌面小组件开发

推荐直接使用系统创建的进行改进,可以使用Handler进行循环调用

private Handler handler=new Handler(){// 使用Handler  定时调用@Overridepublic void handleMessage(@NonNull Message msg) {AppWidgetManager manager = AppWidgetManager.getInstance(context);// 注意保存contextComponentName name = new ComponentName(context, MyAppWidgetProvider.class);//可以获取各种更新所需的信息int[] appWidgetIds = manager.getAppWidgetIds(name);onUpdate(context,manager,appWidgetIds);handler.sendEmptyMessageDelayed(2233,1000);}
};
//views.setOnClickPendingIntent  通过  添加点击意图 监听点击事件

创建集合类小组件

创建集合数据适配器服务(注意该服务必须有android:permission="android.permission.BIND_REMOTEVIEWS"权限)

public class MyViewsFactory extends RemoteViewsService implements RemoteViewsService.RemoteViewsFactory {private Context context;private int[] images;public MyViewsFactory(Context context) {this.context = context;}@Overridepublic void onCreate() {//创建回调images=new int[]{R.drawable.test,R.drawable.test,R.drawable.test,R.drawable.test,R.drawable.test};}@Overridepublic int getCount() {//返回数据数量return images.length;}@Overridepublic RemoteViews getViewAt(int position) {// 返回 设置好的视图RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.image_iten);views.setImageViewResource(R.id.iv_show,images[position%4]);views.setTextViewText(R.id.tv_show,"HelloWorld");return views;}@Overridepublic RemoteViews getLoadingView() {//返回加载中视图return null;}@Overridepublic int getViewTypeCount() {return 1;}@Override// 返回数据服务对象public RemoteViewsFactory onGetViewFactory(Intent intent) {return new MyViewsFactory(context);}
}
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,int appWidgetId) {//更新视图时RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.list_test);Intent intent = new Intent(context,MyRemoteViewsService.class);// 指定数据服务views.setRemoteAdapter(R.id.lv_show,intent);//使用意图指定适配器views.setEmptyView(R.id.lv_show,R.layout.new_app_widget);//设置集合为空的显示appWidgetManager.updateAppWidget(appWidgetId, views);
}

创建图标长按快捷方式

<shortcuts xmlns:android="http://schemas.android.com/apk/res/android"><!--快捷方式的xml配置文件--><shortcutandroid:enabled="true"android:icon="@drawable/test"android:shortcutId="new_photo"android:shortcutShortLabel="@string/app_name"><!--注意这里的文本必须全部使用引用--><intentandroid:action="android.intent.action.VIEW"android:targetClass="com.sk.androidadvancedprogram.MainActivity"android:targetPackage="com.sk.androidadvancedprogram"/></shortcut><!--对应的意图-->
</shortcuts>
<meta-dataandroid:name="android.app.shortcuts"android:resource="@xml/shortcut" /><!--加到启动Activity中-->
ShortcutManager manager= (ShortcutManager) getSystemService(SHORTCUT_SERVICE);
Intent intent = new Intent(this, Test2Activity.class);
intent.setAction(Intent.ACTION_VIEW);
//构建信息
ShortcutInfo info = new ShortcutInfo.Builder(this, "MyId").setShortLabel("动态添加的").setIcon(Icon.createWithResource(this, R.drawable.icon3)).setIntent(intent).build();
//动态添加
manager.setDynamicShortcuts(Collections.singletonList(info));
//manager.requestPinShortcut(info,null);  添加快捷方式到桌面
//根据id移除
//        manager.removeDynamicShortcuts(Collections.singletonList(""));

开发视频壁纸

继承WallpaperService编写逻辑

@Override
public void onVisibilityChanged(boolean visible) {//监听可见性super.onVisibilityChanged(visible);if(visible){ //可见时播放player.start();}else{player.pause();//不可见时暂停}
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {super.onSurfaceCreated(holder);player.setSurface(holder.getSurface());//注意设置Surface  不要player.setDisplay(holder);
}

注册Service(略)

//请求设置壁纸     默认很多手机没有设置壁纸的按钮							壁纸实现类全类名
ComponentName componentName = new ComponentName(getPackageName(), MyWallpaperService.class.getName());
Intent intent = new Intent("android.service.wallpaper.CHANGE_LIVE_WALLPAPER");
intent.putExtra("android.service.wallpaper.extra.LIVE_WALLPAPER_COMPONENT", componentName);
startActivityForResult(intent, 0);

弹幕使用

引入依赖:implementation ‘com.github.ctiao:dfm:0.4.2’

组件:DanmakuSurfaceView(推荐使用性能更好,但是无法透明显示,若需要设置透明度需要使用DanmakuView)

danmaku.setCallback(new DrawHandler.Callback() {// 准备@Overridepublic void prepared() {danmaku.start();}@Overridepublic void updateTimer(DanmakuTimer timer) {}@Overridepublic void danmakuShown(BaseDanmaku danmaku) {}@Overridepublic void drawingFinished() {}
});
context = DanmakuContext.create();//获取上下文对象发送弹幕要用
danmaku.enableDanmakuDrawingCache(true);  //提升屏幕绘制效率
danmaku.prepare(new BaseDanmakuParser() {// 暂时使用普通的弹幕解析器@Overrideprotected IDanmakus parse() {return new Danmakus();}
}, context);  //进行弹幕准备
//创建弹幕对象,TYPE_SCROLL_RL表示从左向右滚动的弹幕
BaseDanmaku danmaku = context.mDanmakuFactory.createDanmaku(BaseDanmaku.TYPE_SCROLL_RL);
danmaku.text = content;
danmaku.padding = 6;
danmaku.textSize = 30;
danmaku.textColor = Color.WHITE;   //弹幕文字颜色
danmaku.time = this.danmaku.getCurrentTime();
if(border)danmaku.borderColor = Color.BLUE;  //弹幕文字边框的颜色
this.danmaku.addDanmaku(danmaku);  //添加一条弹幕

onMeasure支持wrap_content或match_parent

int wSize = MeasureSpec.getSize(widthMeasureSpec);
int wMode = MeasureSpec.getMode(widthMeasureSpec);
if(wMode==MeasureSpec.AT_MOST){//  AT_MOST 是 wrap_contentwSize=size;
}
setMeasuredDimension(wSize,hSize);// 最终设置测量值

ViewGroup通常不需要绘制(若没有设置背景颜色,甚至onDarw方法不会被调用)

自定义View声明xml属性

在attrs.xml中声明属性

<declare-styleable name="MyView"><!--可以使用名字重用--><attr name="color" format="color|reference"/><!--格式可以使用多个 |分割-->
</declare-styleable>

构造器获取属性

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);//指定名字  可以多次获取不同名的
color = array.getColor(R.styleable.MyView_color, Color.GREEN);
array.recycle();//最后一定要回收

自定义ViewGroup

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {measureChildren(widthMeasureSpec,heightMeasureSpec);//先测量子容器 测量完毕Child的getMeasuredXXX才有值// 测试自己 略
}
@Override//注意child.layout设置的坐标相对父容器   传入的是父容器在父容器中的位置信息
protected void onLayout(boolean changed, int l, int t, int r, int b) {int childCount = getChildCount();int top=0;for (int i = 0; i < childCount; i++) {// 布局子容器View child = getChildAt(i);//设置孩子位置  设置完getWidth等才有值  MeasuredWidth获取的包含外边距 getWidth没有child.layout(l,top,l+child.getMeasuredWidth(),top+child.getMeasuredHeight());top+=child.getMeasuredHeight();}
}

Scroller使用

//						      开始位置						相对偏移
scroller.startScroll(getScrollX(),getScrollY(),-getScrollX(),-getScrollY());// 设置滚动
postInvalidate();//触发滚动
@Override//  绘制时会调用该方法
public void computeScroll() {if(scroller.computeScrollOffset()){// 若更新成功//  绝对滚动           滚动是针对 内部坐标的 并不移动 View位置scrollTo(scroller.getCurrX(),scroller.getCurrY());postInvalidate();//调用绘制 循环更新  直到完毕}
}

ListView拾遗

android:divider="@color/colorPrimary" //指定分割线的颜色
android:dividerHeight="1dp"				//指定分割线的高度
android:divider="@null"    //取消分割线
android:scrollbars="none"   //取消滚动条
show.smoothScrollToPosition(1);// 指定滑动到那一条
show.setEmptyView(empty);//设置没有数据时的显示   有数据自动隐藏
show.setOnScrollListener(new AbsListView.OnScrollListener() {// 监听滑动事件@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {}
});
show.setTextFilterEnabled(true);//开启文本过滤 默认关闭  (只对ArrayAdapter有效)
show.clearTextFilter();			//清除过滤词
show.setFilterText(query);		//设置过滤词

安卓坐标系

image-20210602122406202

Canvas常用方法

canvas.save();//状态压栈
canvas.restore();//状态出站
canvas.translate(300,300);//常用画布变换方法
canvas.scale(1,1);
canvas.rotate(i*30);

Bitmap像素操作

//			目标数组 偏移  每行偏移 开始位置  与范围
bitmap.getPixels(datas,0,width,0,0,width,height);// 加载像素信息到datas数组
//  注意 目标 图片必须有透明通道(因为 颜色处理会添加透明通道)
bitmap= Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
bitmap.setPixels(datas,0,width,0,0,width,height);//设置像素信息  对象必须可修改

drawBitmapMesh

//按指定纹理坐标绘制图片 图片 注意宽高是多少块(少一) 顶点位置 先横后竖 先x后y(具体位置,不是0-1) 偏移  颜色相关参数 类似顶点
canvas.drawBitmapMesh(bitmap,WIDTH-1,HEIGHT-1,vers,0,null,0,paint);

使用setXfermode混合模式

canvas.drawBitmap(back,0,0,paint);
int i = canvas.saveLayer(0, 0, 720, 405, paint);//必须保存恢复图层
canvas.drawBitmap(mask,0,0,paint);//绘制遮罩
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));//使用混合模式(也有其他的可以使用)
canvas.drawBitmap(front,0,0,paint);
paint.setXfermode(null);// 取消使用
canvas.restoreToCount(i);//恢复

属性动画,非属性动画

TranslateAnimation animation = new TranslateAnimation(0, 0, 0, 600);//非属性动画  不影响事件
//							属性名(set加不加都行)  若没有对应的set属性可以继承 封装set属性
ObjectAnimator.ofFloat(view,"TranslationY",0,600).setDuration(3000).start();// 属性动画  影响事件响应位置

ValueAnimator所有动画的基类

ValueAnimator animator = ValueAnimator.ofArgb(Color.RED, Color.GREEN, Color.BLUE);//指定关键帧
animator.setDuration(3000);//设置持续时间
animator.setInterpolator(new AccelerateInterpolator());//设置差值器
animator.setEvaluator(new ArgbEvaluator());//设置值解析器 方便生成各种对象
animator.addUpdateListener(animation -> {//监听动画进行计算出的数据 并使用
});
animator.start();//开始动画

View简单动画

view.animate().alpha(0).setDuration(3000);//设置完毕立马生效	xxx() xxxBy()  前者绝对,后者相对

为布局添加动画

LayoutAnimationController controller = new LayoutAnimationController(animation);//animation
show.setLayoutAnimation(controller);//show  ViewGroup   设置子对象添加或删除时的动画
// 也可以直接在布局中指定动画文件		android:layoutAnimation="@anim/layout_anin"

继承Animation自定义动画

public class MyAnimation extends Animation {//继承  Animation自定义动画@Override//初始化方法public void initialize(int width, int height, int parentWidth, int parentHeight) {super.initialize(width, height, parentWidth, parentHeight);setDuration(3000);//初始化设置setFillAfter(true);setInterpolator(new BounceInterpolator());camera=new Camera();centerX=width/2;centerY=height/2;}private Camera camera;private int centerX;private int centerY;@Override//传入动画对象的变换矩阵  与当前进度protected void applyTransformation(float interpolatedTime, Transformation t) {Matrix matrix = t.getMatrix();camera.save();//保存相机camera.rotateY(180*interpolatedTime);//根据进度旋转camera.getMatrix(matrix);//设置变换信息camera.restore();//恢复相机 方便复用matrix.preTranslate(centerX,centerY);matrix.postTranslate(-centerX,-centerY);}
}

安卓生命周期

image-20210602184820841

Intent常用Flag

Intent.FLAG_ACTIVITY_NEW_TASK 创建新栈放启动的Activity

Intent.FLAG_ACTIVITY_SINGLE_TOP 相当于该Activity的launchMode为singleTop

Intent.FLAG_ACTIVITY_CLEAR_TOP 相当于该Activity的launchMode为singleTask

Intent.FLAG_ACTIVITY_NO_HISTORY 启动的Activity若启动其他Activity无法通过返回键返回

获取所有应用信息

List<ApplicationInfo> infos = manager.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);

ApplicationInfo.loadLabel(manager);//获取标签 ApplicationInfo.loadIcon(manager);//获取图标 其他应用信息获取省略

使用ViewStub延迟加载View

<ViewStubandroid:id="@+id/vs_show"android:layout="@layout/show_itme"  //指定要加载的布局文件android:layout_width="wrap_content"android:layout_height="wrap_content"/>
show.setVisibility(View.VISIBLE);//两种方式 都会触发视图加载
LinearLayout inflate = (LinearLayout) show.inflate();//不过这个可以返回根布局

Palette通过Bitmap获取配色方案

Palette.from(bitmap).generate(palette -> {Palette.Swatch swatch = palette.getLightMutedSwatch();//用于获取解析到的配色方案show.setBackgroundColor(swatch.getRgb());//获取其中一种颜色
});

tint(着色)

<ImageViewandroid:tintMode="screen"	//指定混合模式android:tint="@color/colorPrimary"	//指定着色android:src="@drawable/test"android:layout_width="match_parent"android:layout_height="wrap_content"/>

裁剪View

show.setOutlineProvider(new ViewOutlineProvider() {//可以给任意View或嵌套View添加裁剪@Overridepublic void getOutline(View view, Outline outline) {outline.setOval(0,0,720, 720);//设置裁剪规则  还可以设置其他裁剪规则}
});
show.setClipToOutline(true);//开启裁剪	false关闭裁剪

Activity跳转动画

startActivity(new //启动修改Intent(this,Test2Activity.class),ActivityOptions.makeSceneTransitionAnimation(this).toBundle());
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);//目标页面开启动画效果  必须在设置View之前设置
Slide explode = new Slide();//配置动画 默认有	Slide	Fade	Explode可以使用
explode.setDuration(1000);
getWindow().setExitTransition(new Explode());//退出当前页面的动画
getWindow().setEnterTransition(explode);//进入当前页面的动画
getWindow().setReenterTransition(explode);//返回到上个页面的动画(上个页面设置)
getWindow().setReturnTransition(explode);//返回到上个页面的动画(当前页面设置)

Activity带组件跳转

<ImageViewandroid:transitionName="img"	//跳转到界面或该界面都需要有对应的组件并设置相同的transitionNameandroid:src="@drawable/test"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
startActivity(new Intent(this,Test3Activity.class),ActivityOptions.makeSceneTransitionAnimation(this, show,"img").toBundle());//show组件应用	"img"组件的transitionName

ViewAnimationUtils构建环形动画

//									    动画对象                开始位置               开始,结束半径
ViewAnimationUtils.createCircularReveal(show, (int) event.getX(), (int) event.getY(),0, 1000);//返回动画对象

Notification补充

Notification notification = new Notification.Builder(this).setSmallIcon(R.drawable.icon).setContentTitle("标题").setContentText("内容").setAutoCancel(true)//设置点击后自动消失.setVisibility(Notification.VISIBILITY_PRIVATE)//设置显示等级(锁屏是否显示).setFullScreenIntent(PendingIntent.getActivity(this, 2233,//设置为全屏通知new Intent(this, MainActivity.class), Intent.FILL_IN_ACTION), true).build();
manager.notify(2233,notification);//若Id相同不会累加会进行更新

ViewDragHelper使用

public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {super(context, attrs);helper = ViewDragHelper.create(this, new ViewDragHelper.Callback() {@Override// 是否响应child的拖拽事件  pointerId手指Idpublic boolean tryCaptureView(@NonNull View child, int pointerId) {return true;}@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx){if(left<0)left=0;return left;}// 拖动边界控制  child 对应组件       left,top 当前left或top     dx,dy当前移动量@Overridepublic int clampViewPositionVertical(View child, int top, int dy){if(top<0)top=0;return top;}@Override// 释放监听                    View对象               x,y轴速度public void onViewReleased(@NonNull View releasedChild, float xvel, float yvel) {helper.smoothSlideViewTo(releasedChild,0,0);// 移动指定组件到指定位置postInvalidate();//开启动画}@Override//拖拽边界监听        边界类型       手指Idpublic void onEdgeDragStarted(int edgeFlags, int pointerId) {}//  还有其他事件回调 可以监听});helper.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);//设置监听拖拽的边界
}
private ViewDragHelper helper;
@Override
public boolean onInterceptTouchEvent(MotionEvent event){return helper.shouldInterceptTouchEvent(event);
}
// 对拖拽事件进行处理
@Override
public boolean onTouchEvent(MotionEvent event){helper.processTouchEvent(event);return true;
}
@Override
public void computeScroll() {//同样有动画 需要computeScroll循环调用if(helper.continueSettling(true)){postInvalidate();}
}

Visualizer分析音频

visualizer = new Visualizer(player.getAudioSessionId());//获取MediaPlayer音频SessionId(注意必须有录音权限否则报错)
visualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
visualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {@Overridepublic void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {}@Override// 音频数据回调public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {model[0] = (byte) Math.abs(fft[0]);//model长 512  处理数据为对应格式for (int i = 2, j = 1; j < model.length;i+=2,j++) {model[j] = (byte) Math.hypot(fft[i], fft[i + 1]);}}
},Visualizer.getMaxCaptureRate() / 2, true, true);
visualizer.setEnabled(true);//设置开启或关闭
visualizer.release();//最后不要忘了释放资源
visualizer=null;

VolumeShaper控制音量

VolumeShaper.Configuration build = new VolumeShaper.Configuration.Builder().setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR)//线性插值.setCurve(new float[]{0, 0.5f, 1}, new float[]{0, 1, 0.5f})//时间(总时间比例)与音量(原始音量比例)映射.setDuration(3000)//持续时间.build();//构建
VolumeShaper volumeShaper = player.createVolumeShaper(build);
volumeShaper.apply(VolumeShaper.Operation.PLAY);//播放完毕会把音量调整到最后的倍数 多次调用效果会累加

使用MediaProjection进行屏幕投影

startActivityForResult(MediaProjectionManager.createScreenCaptureIntent(),2233);//使用意图请求
MediaProjection=MediaProjectionManager.getMediaProjection(resultCode,data);
//意图响应成功	设置显示的Surface	及大小信息
VirtualDisplay=MediaProjection.createVirtualDisplay("Test",720,1280,1,DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,show.getHolder().getSurface(),null,null);
if(display!=null){//最后不要忘了释放资源display.release();display=null;
}
if(projection!=null){projection.stop();projection=null;
}

aar包

arr包其实就是带res的jar包

使用activity-alias给activity起别名,动态激活改变图标

<activity-aliasandroid:name="com.sunnews.dodo.SplashAliasActivity"		//别名  名字随意android:enabled="false"									//默认启用主的,这个不启用android:icon="@drawable/icon"							//这里设置更改图标android:label="新消息!Do"								// 设置更改后的标题android:targetActivity=".MainActivity">					//实际代理类<intent-filter>//启动类必须的<action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter>
</activity-alias>
PackageManager pm = getPackageManager();//动态设置组件激活状态,用于改变名字与图标(也可以动态设置其它组件的激活状态)
pm.setComponentEnabledSetting(new ComponentName(this, "com.sk.androidmadness.MainActivity"),PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(this, "com.sunnews.dodo.SplashAliasActivity"),PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

ConstraintLayout使用

layout_constraintXXX_toXXXOf 指定上下左右对齐,使用对方的id若与父控件对齐使用parent,一排可以相连,实现均匀布局

圆形布局

app:layout_constraintCircle="@id/btn_1"	//指定圆心
app:layout_constraintCircleAngle="180"	//指定角度
app:layout_constraintCircleRadius="100dp"	//指定半径

百分比设置

android:layout_width="0dp"
android:layout_height="0dp"				
app:layout_constraintHeight_percent="0.1"		//使用百分比布局 注意  对应的layout_height必须设置为0dp
app:layout_constraintWidth_percent="0.5"

比例布局

app:layout_constraintDimensionRatio="1"			//指定宽高比例
android:layout_width="wrap_content"				//按不为0的一方为标准  不能都为0或都不为0
android:layout_height="0dp"

指定偏移

app:layout_constraintHorizontal_bias="0.1"			//指定水平或垂直偏移,对应方向上必须有两个链条绑定两边
app:layout_constraintVertical_bias="0.2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"

安卓上下文菜单

registerForContextMenu(btn);// 为组件长按事件注册菜单
@Override// 可以根据  不同组件响应不同菜单
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {getMenuInflater().inflate(R.menu.my,menu);
}

BroadcastReceiver的生命周期

若使用xml静态注册,每次接收广播,onReceive()执行完毕,都会销毁对象

若使用Java代码,动态注册,不会反复创建销毁

MediaPlayer注意

只有setDataSource()的才需要prepare(),直接通过MediaPlayer.create()不能调用prepare()

若设置setLooping(true);完成回调将不会调用

prepare()或prepareAsync();准备完毕都会回调setOnPreparedListener()方法

处于stop状态可以调用prepare()或prepareAsync()重新进入准备状态,不需要再次设置数据源

image-20210612150535952

OpenGL ES纹理过滤(使用glTexParameterf设置)

GL_NEAREST(临近过滤):取最近的像素点,小图片放大会有颗粒感

GL_LINEAR(线性过滤):根据临近像素进行差值,放大会模糊,不会有颗粒感

OpenGL ES透明效果

gl.glDisable(GL10.GL_DEPTH_TEST);              //关闭深度测试   必须关闭深度测试否则会出现问题
gl.glEnable(GL10.GL_BLEND);                   	//打开混合
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); //使用alpha通道值

WebView补充

暂停,开始与销毁(建议同Activity的生命周期)

WebView.onResume();	//暂停  页面不再刷新响应
WebView.onPause();	//恢复
WebView.destroy();	//释放资源

执行指定JS代码,返回字符串结果(这个不刷新页面,直接使用loadUrl()执行JS需要刷新页面)

evaluateJavascript("window.alert('OKOKKKKKKKKKK')", value -> Log.e("TEST",value));

设置一个意图对应多个应用时选择的界面名称

startActivity(Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),"选择壁纸"));//为意图选择设置标题

image-20210612170037849

AppWidgetProvider

AppWidgetProvider继承BroadcastReceiver在onReceive中会根据ACTION不同字段触发不同方法(可以根据它发送不同广播对其操作)

image-20210612170930900

注意,若需要重写onReceive()一定要先调用父类的

View属性扩展

elevation:指定组件高度,改变默认遮挡关系(可以代码动态改变遮挡关系)

translationZ:效果与elevation类似,不过elevation是绝对高度,translationZ是相对高度

TableLayout使用

<TableLayoutandroid:collapseColumns="1"   // 指定 隐藏列android:shrinkColumns="0"    //指定指定列可以收缩   下标从0开始android:stretchColumns="1,2"	//指定列可以扩展   都可以指定多个android:layout_width="match_parent"android:layout_height="match_parent"><TableRow><Button/><Button/><Button/></TableRow>
</TableLayout>

安卓文本链接

<TextViewandroid:autoLink="phone"   // 指定为电话链接  点击会自动拨到电话android:text="1008311"android:layout_below="@id/tl_show"style="@style/MyText"/>
//		对指定TextView添加链接   匹配模式                         默认会直接把内容拼接到后面
Linkify.addLinks(textView, Pattern.compile("\\d+"),"https://www.baidu.com/s?wd=");

AnalogClock

<AnalogClockandroid:hand_minute="@drawable/hand"    //分针  会以中间做旋转点,建议一半透明android:hand_hour="@drawable/hand"		//时针android:dial="@drawable/bg"				//背景android:layout_below="@id/tv_show"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

ImageSwitcher与TextSwitcher

ImageSwitcher与TextSwitcher都继承自ViewSwitcher

通过setFactory设置创建View(只创建一次,且要创建对应类型的视图)

setOutAnimation,setInAnimation设置内容改变时的动画,通过设置图片资源(ImageSwitcher)获取文本资源(TextSwitcher)改变内容

PopupWindow使用

PopupWindow window = new PopupWindow(this);//创建
window.setContentView(getLayoutInflater().inflate(R.layout.test_menu,null));//设置显示视图
window.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);//设置宽高   必须设置  view  宽高 才能显示
window.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
window.showAsDropDown(btn);//指定显示位置(指定View的下面,有指定偏移与对齐的重载)

PopupMenu使用

PopupMenu menu = new PopupMenu(this, btn2);//创建  并指定锚点对象
menu.inflate(R.menu.test);//加载菜单文件
menu.show();//显示

AsyncTask使用

new AsyncTask<Integer, Integer, String>() {// 参数依次是  初始参数类型  更新UI传递数据类型   最终结果类型@Overrideprotected String doInBackground(Integer... integers) {//处理耗时工作publishProgress(2233);//发布后台进度 调用onProgressUpdate更新前台return "OKKKK";}@Overrideprotected void onPreExecute() {//执行前调用}@Overrideprotected void onPostExecute(String s) {//执行结束调用}@Overrideprotected void onProgressUpdate(Integer... values) {//用于更新进度}
}.execute(100);//开始工作

PreferenceActivity创建设置

Activity继承PreferenceActivity

onCreate中设置xml资源文件(PreferenceScreen) addPreferencesFromResource(R.xml.test);

也可以代码获取Preference对象 findPreference(“test3”);(通过key值查找)

<ListViewandroid:id="@android:id/list"     //注意布局文件中必须有该id对应的ListViewandroid:layout_width="match_parent"android:layout_height="match_parent"/>

常用设置元素(都需要key属性,用于设置保存的key必须唯一)

RingtonePreference 选择铃声

MultiSelectListPreference 多选(必须指定entryValues和entries用于设置数据源)

ListPreference 单选(同样需要指定entryValues和entries)

EditTextPreference 文本设置

SwitchPreference 开关设置

CheckBoxPreference 勾选设置

通过 PreferenceManager.getDefaultSharedPreferences(context); 获取对应map(context是随意的,获取默认的)

intent-filter设置

<intent-filter><!--可以配置多个action,请求意图匹配一个就行--><action android:name="android.intent.action.TEST1"/><action android:name="android.intent.action.TEST2"/><category android:name="android.intent.category.DEFAULT"/><!--配置数据格式与类型,必须满足才能启动没有配置的任意 例如这里需要 dataType为a/b	url为sk://www.sk:80/path 注意需要使用setDataAndType同时设置,否则data与type互相覆盖  --><data android:mimeType="a/b" android:scheme="sk" android:host="www.sk" android:port="80" android:path="/path"/><!--对面可以通过getIntent().getData()获取url信息     属性也可以使用正则表达式指定-->
</intent-filter>

设置SeekBar的样式

android:progressDrawable="@drawable/test"//指定样式文件
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><!--样式文件--><!--指定背景与进度  主要注意id--><item android:id="@android:id/background" android:drawable="@drawable/hand"/><item android:id="@android:id/progress" android:drawable="@drawable/pro"/>
</layer-list>

ContentProvider使用

继承ContentProvider并注册

<providerandroid:name=".part3.MyContentProvider"   android:authorities="com.sk.androidmadness.part3"   <!--指定getContentResolver()操作时的uri-->android:exported="true" /><!--指定向外开放-->

推荐聚合对应的SQLiteOpenHelper进行使用

UriMatcher的使用

matcher=new UriMatcher(UriMatcher.NO_MATCH);//创建并指定映射根uri的id	根uri就是只有域名没有路径的
matcher.addURI(BASE_URL,"cher",ALL);// 添加一个并指定id
//例如   com.sk.androidmadness.part3  "cher"   匹配  content://com.sk.androidmadness.part3/cher
matcher.addURI(BASE_URL,"cher/#",ONE);// 使用通配符#指定数字
int id=matcher.match(uri);//根据url返回匹配id
long id = ContentUris.parseId(uri);// 获取统配符id
Uri uriId = ContentUris.withAppendedId(uri, 12);// 为uri追加id
Uri noId = ContentUris.removeId(uriId);	//移除id

若使用getContentResolver().registerContentObserver注册自己的ContentProvider,需要在自定义ContentProvider发生修改后手动调用getContext().getContentResolver().notifyChange();触发监听事件

IntentService

普通Service仍位于主线程不能执行耗时操作,若要执行耗时操作可以考虑使用IntentService

plurals复数资源文件

<plurals name="apple"><item quantity="one">唯一一苹果</item><item quantity="zero">没有苹果</item><item quantity="other">%d个苹果</item><!--可以使用%d等引用传入变量-->
</plurals>
String quantityString = getResources().getQuantityString(R.plurals.apple, 0,0);//使用必须同时传入 数量与参数

在xml中使用对象

dataBinding {enabled = true   //首先build.gradle开启数据绑定
}
<layout><!--布局使用layout当外布局--><data><!--data内声明变量--><variablename="user"type="com.sk.androidadvancedprogram.part2.User" /></data><LinearLayout ...><TextViewandroid:text="@{user.name}"    <!--下面可以直接使用变量-->style="@style/MyText"/><TextViewandroid:text='@{user.adress??"OK"}'<!--也可以为变量设置默认值(注意引号的使用)-->style="@style/MyText"/></LinearLayout>
</layout>
//使用DataBindingUtil.setContentView设置布局文件代替原来的  setContentView   
ActivityTest5BindingImpl binding = DataBindingUtil.setContentView(this, R.layout.activity_test5);
binding.setUser(user);//返回对象(根据xml动态生成的)设置为准确对象      会自动生成  set变量名  方法设置变量

意图预解析判断是否存在对应的组件处理

Intent intent = new Intent();
ComponentName name = intent.resolveActivity(getPackageManager());//若没有会返回null
//  获取所有匹配意图的 Activity信息
List<ResolveInfo> infos = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);

LocalBroadcastManager本地广播使用

manager = LocalBroadcastManager.getInstance(this);// 获取本地广播
manager.registerReceiver(receiver,new IntentFilter("TEST"));//注册取消注册
manager.unregisterReceiver(receiver);
manager.sendBroadcast(new Intent("TEST"));//发送本地广播

监听SharedPreferences的修改

preferences.registerOnSharedPreferenceChangeListener(listener);//为SharedPreferences添加监听

Handler补充

handler.sendEmptyMessageAtTime();//在指定时间发送
//  除了发送消息  也可以进行 任务调度
handler.postAtTime(runable,time);//  添加定时回调
handler.postDelayed(runable,time);// 添加延时回调
handler.removeCallbacks(runable);//  移除回调

Notification-MessagingStyle

setStyle(new Notification.MessagingStyle("Sk")//  设置显示信息-提示.addMessage("OK1",System.currentTimeMillis()+2222,"LLLL")//每个 都是一条消息.addMessage("OK2",System.currentTimeMillis()+4444,"LLLL").addMessage("OK3",System.currentTimeMillis()+6666,"LLLL"))

Notification自定义布局

RemoteViews views = new RemoteViews(getPackageName(),R.layout.remote);//加载布局
views.setProgressBar(R.id.pb_show,100,30,false);//修改布局
Notification build = new Notification.Builder(this).setContent(views)//设置布局信息.setSmallIcon(R.drawable.test).setWhen(System.currentTimeMillis()).build();

为Activity添加返回按钮

android:parentActivityName=".part2.Part2Activity"//在配置文件指定父Activity就行,不过要保留ActionBar

http://www.ppmy.cn/news/121134.html

相关文章

测试笔记本续航的软件,日常应用对比测试_Intel笔记本电脑_笔记本评测-中关村在线...

基本性能表现了解之后&#xff0c;我们看看四个平台在实际应用中的差异。 对于不少用户而言&#xff0c;买电脑大多是为了办公。而办公则会涉及到Office、Photoshop等软件的应用&#xff0c;接下来我们将针对以下几个项目进行测试&#xff1a; 1.Outlook文件整理&#xff1a;将…

安卓笔记1

安卓笔记 智能手机应用类型按钮菜单右上角小菜单上下文菜单 文本框拨打电话显示弹窗消息弹框界面弹框 界面布局显示过长的文本实现水平居中实现水平和垂直居中使用LinearLayout使用ConstrainLayout模块集中方法 实现北南中布局控件隐藏与显示 ConstraintLayout动画界面的动态替…

评测3款高颜值的安卓azw3阅读器

azw3作为一种特殊的电子书文件格式&#xff0c;如何把azw3文件解析得精美、吸引人观看&#xff0c;且配套的标注、笔记功能齐全&#xff0c;才能称之为比较合格的azw3阅读器。以下是三款适配安卓系统的高颜值azw3阅读器评测结果&#xff1a; 1.Neat Reader 1颜值很高&#xff0…

评测3款高颜值的安卓mobi阅读器

mobi最早是亚马逊的一种电子书格式&#xff0c;适用于在kindle上阅读。而现在其实已经有软件可以支持在电脑、手机上阅读了。作为一种特殊的电子书文件格式阅读器&#xff0c;我觉得把mobi文件解析得精美、吸引人观看&#xff0c;且配套的标注、笔记功能齐全&#xff0c;才能称…

年度最佳Android系统 | 运行在台式机、笔记本手提电脑的安卓Android系统

2019的最佳Android系统 | 运行在台式机、笔记本手提电脑的安卓Android系统 适用于PC 年度的最佳Android操作系统 2019年3月1日 团队技术探索 Android 您是否知道&#xff0c;即使有几个升级版本&#xff0c;如Windows 10和10.1&#xff0c;即使这样&#xff0c;Android应用程…

安卓实现笔记本app

简介 本文实现一个简单的笔记本app&#xff0c;每个笔记有标题和内容&#xff0c;笔记本首页可以浏览有哪些笔记和添加笔记&#xff0c;笔记详情页可以编辑删除笔记。 本app使用Compose ViewModel Room实现&#xff0c;阅读此文前可以先去了解这几个框架。由于使用了这些框架…

Mysql如何定位慢查询(面试题)

Mysql如何定位慢查询&#xff08;面试题&#xff09; 相关概念慢查询分析慢查询工具定位ArthasPrometheusSkywalking Mysql慢查询日志 相关概念 分析MySQL语句查询性能的方法除了使用 EXPLAIN 输出执行计划&#xff0c;还可以让MySQL记录下查询超过指定时间的语句&#xff0c;我…

天地图下载数据的方法

天地图下载数据的方法&#xff0c;目前我知道的&#xff0c;部分数据可以下载&#xff0c;部分数据不提供下载&#xff08;但是有数据&#xff09;具体就不知道原因了&#xff0c;话不多说&#xff1a; 登录全国地理信息资源目录服务系统&#xff08;全国地理信息资源目录服务…