android开发细节——上班实战项目中遇到的棘手问题与解决方案汇总(代码片段)

许英俊 许英俊     2023-03-19     300

关键词:

Paint的细节用法

1、设置笔帽

mPaint.setStrokeCap(Paint.Cap.BUTT);//没有
mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形
mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形

2、设置滤镜

1、模糊遮罩滤镜(BlurMaskFilter2、浮雕遮罩滤镜(EmbossMaskFilter

3、设置线条汇合处

mPaint.setStrokeJoin(Paint.Join.MITER);//锐角
mPaint.setStrokeJoin(Paint.Join.ROUND);//圆弧
mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线

4、文字相关

//获得字符行间距
mPaint.getFontSpacing();
//获得字符之间的间距
mPaint.getLetterSpacing();
//设置文本删除线
mPaint.setStrikeThruText(true);
//设置下划线
mPaint.setUnderlineText(true);
//设置文本大小
mPaint.setTextSize(textSize);
//设置字体类型
mPaint.setTypeface(Typeface.ITALIC);
//加载自定义字体
Typeface.create(familyName, style)
//文字倾斜,官方推荐的-0.25f是斜体
mPaint.setTextSkewX(-0.25f);
//文本对齐方式
mPaint.setTextAlign(Align.LEFT);
mPaint.setTextAlign(Align.CENTER);
mPaint.setTextAlign(Align.RIGHT);
//计算制定长度的字符串(字符长度、字符个数、真实的长度)
int breadText = mPaint.breakText(text, measureForwards, maxWidth, measuredWidth)
float[] measuredWidth = new float[1];
int breakText = mPaint.breakText(str, true, 200, measuredWidth);
Log.i("TAG", "breakText="+breakText+", str.length()="+str.length()+", measredWidth:"+measuredWidth[0]);
//获取文本的矩形区域(宽高)
mPaint.getTextBounds(text, index, count, bounds)
//获取文本的宽度(比较粗略的结果)
float measureText = mPaint.measureText(str);
//获取文本的宽度(比较精准的)
float[] measuredWidth = new float[10];
int textWidths = mPaint.getTextWidths(str, measuredWidth);
Log.i("TAG", "measureText:"+measureText+", textWidths:"+textWidths);

5、基线相关

FontMetrics fontMetrics = mPaint.getFontMetrics();
//绘制文本在View的左上角(基线Y的计算公式)
float baselineY = top(已知) - fontMetrics.top;
//绘制文本的在View的中间(基线Y的计算公式)
float baselineY = centerY(已知)+ (fontMetrics.bottom-fontMetrics.top)/2 - fontMetrics.bottom;
//绘制文本的在View的中间(或者是这样的)
float baselineY = centerY(已知)+ (mPaint.descent()+mPaint.ascent())/2;

6、渲染相关

LinearGradient线性渲染
RadialGradient环形渲染、可做水波纹效果,充电水波纹扩散效果、调色板
SweepGradient梯度渲染(扫描渲染)、可做微信等雷达扫描效果
ComposeShader组合渲染

7、ColorMatrix(五阶矩阵)

//色彩的平移运算(加法运算)
//色彩的缩放运算(乘法运算)

//构造方法
ColorMatrix matrix = new ColorMatrix(new float[]);
ColorMatrix matrix = new ColorMatrix();
matrix.set(src)

//设置色彩的缩放函数
matrix.setScale(1, 1, 1.4f, 1);

//设置饱和度(1,是原来不变;0灰色;>1增加饱和度)
matrix.setSaturation(progress);

//色彩旋转函数(axis,代表绕哪一个轴旋转,0红色,1绿色,2蓝色,degrees:旋转的度数)
matrix.setRotate(0, progress);

//ColorFilter使用的子类
ColorMatrixColorFilter:色彩矩阵的颜色顾虑器。
LightingColorFilter:过滤颜色和增强色彩的方法。(光照颜色过滤器)
PorterDuffColorFilter:图形混合滤镜(图形学的一个理论飞跃)

Canvas.drawPath

将文本画在Path上,可以形成圆弧的文本

Path path = new Path();
//Path.Direction.CW,沿外环;Path.Direction.CCW,沿内环
path.addCircle(500, 500, 200, Path.Direction.CW);
mPaint.setTextSize(50);
// 绘制路径
canvas.drawPath(path, mPaint);
String text = "大家好,我是Hensen";
canvas.drawTextOnPath(text, path, 0f, 0f, mPaint);

Home键回来后会先拉起闪屏的问题

在某些机型上,第一次安装点击icon启动应用,然后home键退到后台,再次点击icon启动应用,会先拉起闪屏的问题

class SplashActivity : BaseVMActivity<SplashViewModel>() 

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        
        /**
         * 如果此页面不是任务栈中根activity,说明应用有其他activity存在,此时不需要拉起splash页面,任务栈被切到前台会自动显示栈顶activity
         */
        if (!isTaskRoot) 
            //如果是外部跳转的Intent也会跑到这里,需要特殊处理
            SchemaUtils.gotoArouter(intent)
            finish()
            return
        
        setContentView(R.layout.activity_splash)
    

java.lang.NoSuchMethodError

在我们崩溃系统中会看到这种崩溃

java.lang.NoSuchMethodError: No static method Desc(I)Ljava/lang/String; in class Lcom/yy/platform/baseservice/ConstCode$SrvResCode; or its super classes (declaration of 'com.yy.platform.baseservice.ConstCode$SrvResCode' appears in /data/app/com.yy.yinfu-0eluUMz7kO0sb_RhAZpHNw==/split_lib_dependencies_apk.apk!classes2.dex)

从崩溃的日志上翻译出来的意思是,找不到该类的方法,但是又在classes2.dex发现了这个类的方法,这是因为在多Dex的情况下,我们启动需要的class都需要放在maindex中,由于这个类放在了classes2.dex,所以报出找不到的崩溃。由于我们是Debug包,所以不加处理,但是在Release包中我们对混淆过后的这个类放在maindex中

release 
    ......
    multiDexKeepProguard file('multidex.keep')
    ......

multidex.keep中加入我们找不到的class即可

-keep class com.xxx.xxx.xxx$xxxx

Fragment嵌套SurfaceView切换时闪黑屏

最近在开发的时候用的是ViewPager+Fragment进行底部Tab页的切换,但在最后一个Tab中Fragment嵌入了SurfaceView,用于Flutter开发,不料在切换Tab的时候SurfaceView的Tab会导致其他Tab切换过去的时候显示黑屏或者白屏等情况(黑屏、白屏主要取决你设置的Theme),解决方法是在SurfaceView上进行这两句话设置

setZOrderOnTop(true);    
setZOrderMediaOverlay(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);

RecyclerView实现点击拖拽而不是长按拖拽

最近做项目,需要在3张图片中,左右拖动能更换位置,再此,封装了一个左右拖拽的辅助类,将RecyclerView设置不能左右滑动,但是点击就能左右拖动Item,并交换左右图片

class ListeningKonwPeopleRecyclerViewController(var context: Context,
                                                var recyclerView: RecyclerView,
                                                var data: ArrayList<LisnteningKonwPeopleInfo>) 

    var helper: ItemTouchHelper? = null
    var adapter: ListeningKonwPeopleCoverAdapter? = null
    var layoutManager: LinearLayoutManager? = null

    init 
        layoutManager = object : LinearLayoutManager(context, HORIZONTAL, false) 
            override fun canScrollHorizontally(): Boolean 
                return false
            
        

        adapter = ListeningKonwPeopleCoverAdapter(data)
        recyclerView.layoutManager = layoutManager
        recyclerView.adapter = adapter

        helper = ItemTouchHelper(
                object : ItemTouchHelper.Callback() 
                    override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int 
                        val dragFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT//拖拽
                        return ItemTouchHelper.Callback.makeMovementFlags(dragFlags, 0)
                    

                    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean 
                        //滑动事件
                        Collections.swap(data, viewHolder.adapterPosition, target.adapterPosition)
                        adapter?.notifyItemMoved(viewHolder.adapterPosition, target.adapterPosition)
                        return false
                    

                    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) 
                        //侧滑事件
                    

                    override fun isLongPressDragEnabled(): Boolean 
                        return false
                    
                )
        helper?.attachToRecyclerView(recyclerView)
    

    fun attach() 
        adapter?.setOnTouchClickListener(
                object : ListeningKonwPeopleCoverAdapter.OnTouchClickListener 
                    override fun onTouchClick(info: LisnteningKonwPeopleInfo, holder: ListeningKonwPeopleCoverAdapter.VH) 
                        helper?.startDrag(holder)
                    
                )
    

    fun dettach() 
        adapter?.setOnTouchClickListener(
                object : ListeningKonwPeopleCoverAdapter.OnTouchClickListener 
                    override fun onTouchClick(info: LisnteningKonwPeopleInfo, holder: ListeningKonwPeopleCoverAdapter.VH) 
                        return
                    
                )
    

Adapter中设置OnTouchClickListener的实现

override fun onBindViewHolder(holder: VH, position: Int) 
    ......
    
    holder.itemView.setOnTouchListener  v, event ->
        if (event.action == MotionEvent.ACTION_DOWN) 
            mOnTouchClickListener?.onTouchClick(info, holder)
        
        return@setOnTouchListener false
    

使用点击拖拽功能

activity?.let 
    recyclerViewController = ListeningKonwPeopleRecyclerViewController(it, view.rv_cover, data)
    recyclerViewController?.attach()

取消点击拖拽功能

activity?.let 
    recyclerViewController = ListeningKonwPeopleRecyclerViewController(it, view.rv_cover, data)
    recyclerViewController?.dettach()

键盘弹起不会顶上去视图

键盘弹起要让视图跟着弹起解决方案太多,但是有个比较简单的方案,可以适合大多数场景,这场适合于键盘盖住视图的情况下使用,在盖住的布局中使用android:fitsSystemWindows="true"即可

<com.yy.mobile.memoryrecycle.views.YYLinearLayout
        android:id="@+id/input_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="@color/common_color_11"
        android:fitsSystemWindows="true"
        android:gravity="center"
        android:minHeight="@dimen/moment_chat_input_height"
        android:orientation="vertical"
        android:visibility="gone">

为什么View.startAnimation不起作用?

看了一下View里面的源码,发现确实有一些地方判断了如果不是visible的,那么就不调用invalidate方法,也就不会去处理Animation的事情。以后startAnimation的时候,—定要选─个总是可见的View哦

正确的用法

private fun startShowAnimation(from: AnimationFrom,
                               animationView: View,
                               centerX: Float,
                               centerY: Float) 
    animationView.visibility = View.VISIBLE
    val scaleAnimation = ScaleAnimation(0f, 1f, 0f, 1f, centerX, centerY)
    scaleAnimation.duration = 250
    scaleAnimation.setAnimationListener(object : Animation.AnimationListener 
        override fun onAnimationRepeat(animation: Animation?) 
        

        override fun onAnimationEnd(animation: Animation?) 
        

        override fun onAnimationStart(animation: Animation?) 
        
    )
    animationView.startAnimation(scaleAnimation)

横竖屏切换沉浸式失效

  1. 查问题时间:3天,问题与window焦点触摸有关
  2. 需要效果:在沉浸式模式下,横屏需要隐藏导航栏,竖屏需要显示导航栏
  3. 出现问题:在横屏,点击屏幕后导航栏消失,切换为竖屏时,沉浸式效果失效,导航栏消失,只要点击屏幕即可重新恢复沉浸式
  4. 解决问题:在沉浸式实现中套一层getWindow().getDecorView().post(new Runnable() 即可解决
private void initImmersive() 
    if (isLandscape()) 
        //增加沉浸式
        ImmersionBar.with(this)
                .navigationBarEnable(false)
                .transparentStatusBar()
                .statusBarDarkFont(false)
                .init();
        //横屏需要隐藏底部导航栏
        getWindow().getDecorView().post(new Runnable() 
            @Override
            public void run() 
                if (!checkActivityValid()) 
                    return;
                
                if (getContext() instanceof Activity && NavBarUtils.hasNavBar(getContext())) 
                    NavBarUtils.hideBottomNav((Activity) getContext());
                
            
        );
     else 
        getWindow().getDecorView().post(new Runnable() 
            @Override
            public void run() 
                if (!checkActivityValid()) 
                    return;
                
                if (getContext() instanceof Activity) 
                    //增加沉浸式
                    ImmersionBar.with((Activity) getContext())
                            .navigationBarEnable(false)
                            .transparentStatusBar()
                            .statusBarDarkFont(false)
                            .init();
                
            
        );
    

在特殊情况下,设置View的Gone无效

  • 背景:在拉取视频流的时候,将视频流的父布局Gone掉,但视频流拉流成功后,视频流的布局还是会出来,也就是说父布局Gone掉,子布局因为拉流成功而显示出来
  • 原因:由于某些代码调用了父布局Gone,但流的视频布局是等拉流成功后才addview进来,导致父布局先Gone,流布局add进来后是visible,这时,流就会出来
  • 解决:调整下逻辑,先等流进来后,再Gone掉,或者不要拉流的逻辑

在水滴屏情况下,横屏键盘适配处理

  • 背景:在水滴屏手机上,发现横屏之后,弹起的键盘无法延伸到水滴屏内,跟水滴屏相隔有条缝,但是整个布局依然在水滴屏内
  • 原因:水滴屏需要额外适配
  • 解决:在横竖屏的时候进行水滴屏的设置处理
@Override
public void onConfigurationChanged(Configuration newConfig) 
    super.onConfigurationChanged(newConfig);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) 
        if (mIsLandscape) 
            getWindow().getAttributes().layoutInDisplayCutoutMode =
                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
         else 
            getWindow().getAttributes().layoutInDisplayCutoutMode =
                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        
    

引用 https://blog.csdn.net/guolin_blog/article/details/103112795

Android 9.0系统中提供了3种layoutInDisplayCutoutMode属性来允许应用自主决定该如何对刘海屏设备进行适配:

  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT:这是一种默认的属性,在不进行明确指定的情况下,系统会自动使用这种属性。这种属性允许应用程序的内容在竖屏模式下自动延伸到刘海区域,而在横屏模式下则不会延伸到刘海区域
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:这种属性表示,不管手机处于横屏还是竖屏模式,都会允许应用程序的内容延伸到刘海区域
  • LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:这种属性表示,永远不允许应用程序的内容延伸到刘海区域

AS一打开hprof文件就出现卡机,卡死

解决:在AS设置中,将内存变大

在关闭页面的onPause周期中处理销毁逻辑

  • 遇到问题:关闭当前页面的时候,生命周期会先走底下页面的onResume,再走关闭页面的onDestroy方法
  • 具体场景:客户端进入播放器页面播放视频,在关闭视频页的时候,onDestroy执行打开视频声音,首页不需要视频声音,在onResume的时候首页启动静默播放,导致有声音出现
  • 解决方法:在onPause中做销毁页面的逻辑,具体如下代码
@Override
protected void onPause() 
    super.onPause();
    if (isFinishing()) 
       //退出当前页
       ......
    

View的Touch事件区域判断

/**
 * 判断触摸事件是否在对应的view上
 *
 * @param e 触摸事件
 * @param v 对应的View
 * @return true 当且仅当触摸事件e是发生在v上面
 */
public static boolean isOnView(MotionEvent e, View v) 
    Rect r = new Rect();
    v.getGlobalVisibleRect(r);
    returnandroid开发细节——上班实战项目中遇到的棘手问题与解决方案汇总(代码片段)

Paint的细节用法1、设置笔帽mPaint.setStrokeCap(Paint.Cap.BUTT);//没有mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形2、设置滤镜1、模糊遮罩滤镜(BlurMaskFilter)2、浮雕遮罩滤镜(EmbossMaskFilter)3、设... 查看详情

android开发过程中遇到的棘手的问题笔记(sp引起的anr,4g网络请求慢,app启动白屏)(持续更新)(代码片段)

前言经历过面试的人应该都知道,一般我们在进行技术面试的时候,面试官都会问你,在项目开发中遇到过什么棘手的问题?最后是怎么解决的?本人之前就问到过好几次,可是由于准备不足,一时之... 查看详情

面试必问_你在开发过程中有没有遇到什么棘手的问题,是怎么解决的

文章目录文章目录文章目录开发过程中有没有遇到什么棘手的问题,是怎么解决的1、表单提交的时候,刷新一下,会重新提交一次的问题,这样造成了性能的浪费,因为重新提交没啥用,还会重新查一次... 查看详情

flutter混合开发实战

...个新的需求需要使用Flutter单独开一个模块集成到原有的android项目中下面分享一下如何集成现有的项目和如何继承以及碰到的问题1.首先第一步修改gradle因为Flutter当前仅支持为x86_64,armeabi-v7a和arm64-v8a构建预编(AOT)的库所以我... 查看详情

android组件化开发快速上手,附强化实战

在Android开发的过程中,我们常常会遇到这样的问题:随着app业务的壮大,模块越来越多,项目的代码量非常多,这是无论是编译、测试还是稍微做下改动,都要动整个工程,耗时耗力不说,还会让... 查看详情

面试必问_你在开发过程中有没有遇到什么棘手的问题2,是怎么解决的

系列文章目录文章目录系列文章目录找不到bean找不到bean明明有,为什么找不到呢这是因为查找bean需要到IOC容器里面去找https://blog.csdn.net/qq_41753340/article/details/122799665 查看详情

android项目实战android开发进阶学习

【Android项目实战】Android开发进阶学习——壹节课搞定NDK身份证识别技术项目实战 查看详情

手机app软件开发有什么需要注意的细节?

...似很小,且很容易被忽略的问题,正是这些手机应用软件开发小问题,一次次的撩拨用户的耐心,让用户对你的产品心生怨念。刚出道的朋友没有经过实战,对细节注意不多,往往都会遇到类似的问题,强调多次后,觉得不如写... 查看详情

android组件化强化实战,手把手教你搭建组件化项目架构,进阶高级开发

作为一个Android开发人员,我时常图省事将单工程撸到底,但随着项目越来越大,问题也逐渐凸显出来,比如定位查找速度慢、维护成本不断增加、代码复用性差等,大大降低了开发效率。因此,我开始尝... 查看详情

git实战

...节?? 项目版本的发布??    版本控制是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一。  版本控制最主要的功能就是追踪文件的变更。它将什么时候、什... 查看详情

android开发中遇到关于byte位运算通信协议类项目的文档解读分析(代码片段)

Android开发中经常会遇到Byte位运算通信协议的项目,一个简单的Byte可能隐藏着极其复杂的数据,需要根据既定的协议来解析和封装。那么开发中要怎么解决这类项目呢,还是要多熟悉文档和源码。这类项目笔者15年的... 查看详情

android实战----开篇(附android开发常用的开源框架)

终于还是要进行Android实战开篇系列了,年初就说过要进行这个系列专题。Android是一个很大的话题,作为非资深Android开发工程师(只是两年的Android系统开发及两年app开发经验而已),这里只是将Android开发所需... 查看详情

项目的难点

...,一个项目中到底什么样的问题才算是棘手的问题?项目开发前业务流程梳理(产品意识)技术架构设计(架构师)项目开发中处理开发中的技术难点合理安排开发人力,保证项目进度对产品提出的需求做出质疑和把控,保证需... 查看详情

java由浅入深开发企业级电商项目大牛实战开发电商后台项目实战视频教程

...在这过程中大型架构演进思想以及代码演进细节。第2章开发环境安装与配置讲解、实操(linux平台【推荐】或windows平台)考虑到学习这门课程的同学自己的电脑系统有的可能是Li 查看详情

winform多语言版本实战项目演练

...读者就有一千个哈姆雷特!如果您工作中恰好也遇到这种开发需求,但是为了一个自上手简单、维护方便、扩展性强的WinForm多语言版本技术方案而感到烦恼的话,那么很高兴恭喜你看到了阿笨的本次分享课程;阿笨结合的是自... 查看详情

winform多语言版本实战项目演练

...读者就有一千个哈姆雷特!如果您工作中恰好也遇到这种开发需求,但是为了一个自上手简单、维护方便、扩展性强的WinForm多语言版本技术方案而感到烦恼的话,那么很高兴恭喜你看到了阿笨的本次分享课程;阿笨结合的是自... 查看详情

android快乐贪吃蛇游戏实战项目开发教程-06虚拟方向键绘制方向键箭头

本系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html本系列教程项目源码GitHub地址:https://github.com/jackchengyujia/HappySnake一、本文概述在上篇教程中,我们画了4个背景三角形,并且实现了点击变色的按钮效果。在本篇教程... 查看详情

android开发错误——androidstudio中遇到过的错误问题与解决方案汇总(代码片段)

(1)编译时发生 解决方法:1、在最外层的Gradle中,删除如下的语句 (2)编译时发生 解决方法:1、在最外层的Gradle中,删除如下的语句 (3)编译时发生 解决方法:1、你的项目中存在中文命名,将项目中对... 查看详情