美文网首页Android
android 小技巧

android 小技巧

作者: 客观开发者 | 来源:发表于2021-12-13 09:52 被阅读0次

    1,字体随着宽度大小变化
    https://github.com/ZwwwDamon/AutoScaleTextView
    textview 一个不能显示全,就换另一行了
    方案:

    2,字体大小适配
    AppCompatTextView 的使用。

     android:autoSizeMinTextSize="8dp"
        android:autoSizeTextType="uniform"
        android:autoSizeMaxTextSize="18dp"
        android:maxLines="1"
    
    

    第三方me.grantland.widget.AutofitTextView

    3,json 数据解析 android utils 里面的json 解析方案

     var bean: Entity<RecordeDetail> =
                    GsonUtils.fromJson(
                        data,
                        字典
                        GsonUtils.getType(
                            Entity::class.java, GsonUtils.getType(RecordeDetail::class.java)
                        )
                        数组
                        GsonUtils.getType(
                            Entity::class.java, GsonUtils.getListType(RecordeDetail::class.java)
                        )
                    )
    

    4,圆角
    https://github.com/RaphetS/RoundImageView
    https://blog.csdn.net/ldld1717/article/details/106652831

    5,blog -android
    https://www.gcssloop.com/#blog
    6.RecyclerView的滑动监听
    滑动或者向上滑动监听
    http://www.demodashi.com/demo/17113.html
    https://www.jianshu.com/p/a392ab3b8aec

    7.清明节 的时候app 变灰色
    国家公祭日 变灰色。。
    勿忘国耻!!!

    
    //        这个是界面变成灰色处理
            var time = TimeUtils.getStringTodayMY()
    //         当每年的清明节的时候,app 变成灰色
            time = time.replace(Regex("0"), "")
            if (time == "4-4") {
                val paint = Paint()
                val cm = ColorMatrix()
                cm.setSaturation(0F)
                paint.colorFilter = ColorMatrixColorFilter(cm)
                window.decorView.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
            } else {
                val paint = Paint()
                window.decorView.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
            }
    

    8,BubbleView和TipView的使用,是app 更加友好

    1. 提示音
    //播放系统提示音提示下载完成
                            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
                            Ringtone r = RingtoneManager.getRingtone(application, notification);
                            r.play();
    

    10,个别机型 界面直接跳转回会跳动,闪烁情况。
    去掉动画就行了。
    theme.xml中配置
    <item name="android:windowAnimationStyle">@style/Animation</item>

    <style name="Animation">
    <item name="android:activityOpenEnterAnimation">@null</item>
    <item name="android:activityOpenExitAnimation">@null</item>
    <item name="android:activityCloseEnterAnimation">@null</item>
    <item name="android:activityCloseExitAnimation">@null</item>
    <item name="android:taskOpenEnterAnimation">@null</item>
    <item name="android:taskOpenExitAnimation">@null</item>
    <item name="android:taskCloseEnterAnimation">@null</item>
    <item name="android:taskCloseExitAnimation">@null</item>
    <item name="android:taskToFrontEnterAnimation">@null</item>
    <item name="android:taskToFrontExitAnimation">@null</item>
    <item name="android:taskToBackEnterAnimation">@null</item>
    <item name="android:taskToBackExitAnimation">@null</item>
    </style>

    想要加个别的有动画,就单独页面去配置

    11,蒲公英统计的坑,和版本更新。
    之前用他的版本更新,用自己的,但是统计还用,但是,版本更新的方法还得留着。坑死你。

    12, //导入SDK相关依赖jar、aar
    implementation fileTree(include: ['.jar'], dir: 'libs')
    implementation fileTree(include: ['
    .aar'], dir: 'libs')

    13.ndk so 文件
    配置
    ndk {
    abiFilters 'x86','armeabi-v7a',"arm64-v8a" //不支持armeabi
    }

    android.useDeprecatedNdk=true

    这个易忘记
    14
    kotlin 单例

    //    private object Single {
    //        var userId by SpUtils("userId", 0)
    //        val sin: AppDataBase = Room.databaseBuilder(
    //            BaseApp.context!!,
    //            AppDataBase::class.java,
    //            "material_cart$userId.db"
    //        )
    //            .allowMainThreadQueries()
    //            .build()
    //    }
    

    ...

    companion object {
    
        @Volatile private var INSTANCE: AppDatabase? = null
    
        fun getInstance(context: Context): AppDatabase =
                INSTANCE ?: synchronized(this) {
                    INSTANCE ?: buildDatabase(context).also {
                        INSTANCE = it
                    }
                }
    
        private fun buildDatabase(context: Context) =
    
                Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "Fazendao.sqlitedb"
                )
                .addMigrations(Migration1315)
                .build()
    
        }
    }
    

    object 基本上就是一个单例了。,上面反而复杂了。

    15 ,下拉刷新
    配置

    SmartRefreshLayout.setDefaultRefreshFooterCreator((context, layout) -> {
                layout.setEnableFooterFollowWhenNoMoreData(true);
                layout.setEnableFooterTranslationContent(true);
                layout.setFooterHeight(153f);
                layout.setFooterTriggerRate(0.6f);
                return new ClassicsFooter(context).setDrawableSize(20);
            });
    

    设置更多 无数据的情况ui
    16,java8 android应用

    var d = list1.stream().filter { data -> data.whetherRequire == 1 }
                                    .collect(Collectors.toList())
                                
     var d = list1.stream().filter(object : Predicate<ResultFileEnclosureData>{
                          override fun test(t: ResultFileEnclosureData): Boolean {
                                 return data.whetherRequire == 1
    //                                    return true
               }
        })
       .collect(Collectors.toList())
    
    1. 携程的使用。
      回到的函数,让其就行回调使用
    suspend fun getLocaltion(): AMapLocation {
            return return suspendCoroutine { cancellableContinuation ->
                LocationUtils.instance().requestLocation { a ->
                    cancellableContinuation.resume(a)
                }
            }
        }
    

    suspendCoroutine 的使用。错误的使用。。。

    https://kotlinlang.org/docs/coroutines-basics.html#your-first-coroutine
    官网对runBlocking launch suspend 一起使用,启动顺序 都很好案例。

    让其 网络获取更加简单 + okhttp

    1. aroute 的使用
      极光推送demo 里面看的思想很好。。
    private void initRouter() {
            if (BuildConfig.DEBUG) {
                ARouter.openLog();
                ARouter.openDebug();
            }
            ARouter.init(this);
    
            ARouter.getInstance().build(ServiceConstant.SERVICE_SHARE).navigation();
            ARouter.getInstance().build(ServiceConstant.SERVICE_LINK).navigation();
            ARouter.getInstance().build(ServiceConstant.SERVICE_PUSH).navigation();
            ARouter.getInstance().build(ServiceConstant.SERVICE_VERIFY).navigation();
            ARouter.getInstance().build(ServiceConstant.SERVICE_UNION).navigation();
        }
    

    这样初始化很多东西了。。。都可以很好的解决问题

    @Route(path = ServiceConstant.SERVICE_PUSH)
    public class PushServiceImpl implements InitService {
    
        private static final String TAG = "PushServiceImpl";
    
    
        @Override
        public void init(Context context) {
    
            Log.i(TAG, "PushServiceImpl init");
    
            JPushInterface.setDebugMode(true);
            JPushInterface.init(context);
        }
    }
    
    

    主要是初始化情况。。。

    19、androd studio 虚拟机创建成功
    虚拟机在c 盘上。我c盘内存储存不足了。修改地方就可以了
    Android Studio avd虚拟机存储位置更改
    https://blog.csdn.net/dinggangchi7733/article/details/80211051
    创建一个 文件夹,写一个新建一个系统变量,名称为ANDROID_SDK_HOME,值为新位置,如
    将c盘的.android 全部拷贝过去就可以了。

    20.俩个activity 中公用一个viewmodel
    https://github.com/LucasDevelop/CustomView/blob/master/app/src/main/java/com/cj/customwidget/page/viewmodel/VM1Activity.kt
    https://github.com/LucasDevelop/CustomView/blob/master/app/src/main/java/com/cj/customwidget/page/viewmodel/VM2Activity.kt

    @Retention(AnnotationRetention.RUNTIME)
    @Target(AnnotationTarget.FIELD)
    annotation class VMScope(val scopeName:String) {
    }
    
    
    import androidx.activity.ComponentActivity
    import androidx.lifecycle.*
    
    /**
     * @package    com.cj.customwidget.page.viewmodel
     * @author     luan
     * @date       2020/11/2
     * @des
     */
    
    private val vMStores = HashMap<String, VMStore>()
    
    fun ComponentActivity.injectViewModel() {
        //根据作用域创建商店
        this::class.java.declaredFields.forEach { field ->
            field.getAnnotation(VMScope::class.java)?.also { scope ->
                val element = scope.scopeName
                var store: VMStore
                if (vMStores.keys.contains(element)) {
                    store = vMStores[element]!!
                } else {
                    store = VMStore()
                    vMStores[element] = store
                }
                store.register(this)
                val clazz = field.type as Class<ViewModel>
                val vm = ViewModelProvider(store, VMFactory()).get(clazz)
                field.set(this, vm)
            }
        }
    }
    
    class VMStore : ViewModelStoreOwner {
    
        private val bindTargets = ArrayList<LifecycleOwner>()
        private var vmStore: ViewModelStore? = null
    
        fun register(host: LifecycleOwner) {
            if (!bindTargets.contains(host)) {
                bindTargets.add(host)
                host.lifecycle.addObserver(object : LifecycleEventObserver {
                    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                        if (event == Lifecycle.Event.ON_DESTROY) {
                            host.lifecycle.removeObserver(this)
                            bindTargets.remove(host)
                            if (bindTargets.isEmpty()) {//如果当前商店没有关联对象,则释放资源
                                vMStores.entries.find { it.value == this@VMStore }?.also {
                                    vmStore?.clear()
                                    vMStores.remove(it.key)
                                }
                            }
                        }
                    }
                })
            }
        }
    
        override fun getViewModelStore(): ViewModelStore {
            if (vmStore == null)
                vmStore = ViewModelStore()
            return vmStore!!
        }
    }
    
    class VMFactory : ViewModelProvider.Factory {
        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            return modelClass.newInstance()
        }
    
    }
    

    https://www.jianshu.com/p/f211ca175a25
    21、viewpager2
    感觉和recyclerview 一样。
    就是item view 就必须高度是全屏。
    默认是横向的。竖向

    viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
    recyclerview 也可以和viewpager 一样。修改LayoutManager 就可以。

    package com.example.viewpager2.recyclerview;
    
    import android.content.Context;
    import android.util.Log;
    import android.view.View;
    
    import androidx.annotation.NonNull;
    import androidx.recyclerview.widget.LinearLayoutManager;
    import androidx.recyclerview.widget.PagerSnapHelper;
    import androidx.recyclerview.widget.RecyclerView;
    
    public class MyLayoutManager extends LinearLayoutManager implements RecyclerView.OnChildAttachStateChangeListener {
    
        //根据这个参数来判断当前是上滑  还是下滑
        private int mDrift;
        //传进来的监听接口类
        private OnViewPagerListener onViewPagerListener;
        //解决吸顶或者洗低的对象
        private PagerSnapHelper pagerSnapHelper;
    
    
        public MyLayoutManager(Context context) {
            super(context);
        }
    
        public MyLayoutManager(Context context, int orientation, boolean reverseLayout) {
            super(context, orientation, reverseLayout);
            pagerSnapHelper = new PagerSnapHelper();
        }
    
    
        /**
         * 当MyLayoutManager完全放入到RecyclerView中的时候会被调用
         */
        @Override
        public void onAttachedToWindow(RecyclerView view) {
            view.addOnChildAttachStateChangeListener(this);
            pagerSnapHelper.attachToRecyclerView(view);
            super.onAttachedToWindow(view);
        }
    
        @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
            mDrift = dy;
            return super.scrollVerticallyBy(dy, recycler, state);
        }
    
        @Override
        public boolean canScrollVertically() {
            return true;
        }
    
        /**
         * 将Item添加进来的时候  调用这个方法
         */
        @Override
        public void onChildViewAttachedToWindow(@NonNull View view) {
            if (mDrift > 0) {
                //向上滑
                if (onViewPagerListener != null) {
                    //如果是向上滑动的时候  就选中当前itemView下一个item
                    onViewPagerListener.onPageSelected(view);
                }
            } else {
                //向下滑
                if (onViewPagerListener != null) {
                    //如果是向上滑动的时候  就选中当前itemView下一个item
                    onViewPagerListener.onPageSelected(view);
                }
            }
        }
    
        /**
         * 监听滑动的状态
         */
        @Override
        public void onScrollStateChanged(int state) {
            switch (state) {
                case RecyclerView.SCROLL_STATE_IDLE:
                    //现在拿到的就是当前显示的这个item
                    View snapView = pagerSnapHelper.findSnapView(this);
                    assert snapView != null;
                    if (onViewPagerListener != null) {
                        onViewPagerListener.onPageSelected(snapView);
                    }
                    break;
            }
            super.onScrollStateChanged(state);
        }
    
        /**
         * 将Item移除出去的时候  调用这个方法
         */
        @Override
        public void onChildViewDetachedFromWindow(@NonNull View view) {
            Log.e("EEEEEEEEE", "22222222222222222");
            if (mDrift >= 0) {
                //向上滑
                if (onViewPagerListener != null) {
                    onViewPagerListener.onPageRelease(view);
                }
            } else {
                //向下滑
                if (onViewPagerListener != null) {
                    onViewPagerListener.onPageRelease(view);
                }
            }
        }
    
    
        public void setOnViewPagerListener(OnViewPagerListener onViewPagerListener) {
            this.onViewPagerListener = onViewPagerListener;
        }
    }
    
    import android.view.View;
    
    public interface OnViewPagerListener {
        //停止播放的监听方法
        void onPageRelease(View itemView);
    
        //播放的监听方法
        void onPageSelected(View itemView);
    }
    

    22、FFmpeg 命令
    https://blog.csdn.net/wenmingzheng/article/details/88373192
    -to 截到视频的哪个时间点结束。00:00:15是到视频的第15s结束。

    如果用-t 表示截取多长的时间如 上文-to 换位-t则是截取从视频的第10s开始,截取15s时长的视频。即截出来的视频共15s.

    注意的地方是:

    如果将-ss放在“-i 源文件名”后面则-to的作用就没了,跟-t一样的效果了,变成了截取多长视频。一定要注意-ss的位置。

    参数解析

    -vcodec copy表示使用跟原视频一样的视频编解码器。

    -acodec copy表示使用跟原视频一样的音频编解码器。

    -i 表示源视频文件

    -y 表示如果输出文件已存在则覆盖。

    23、kotlin 里面的深拷贝

    24、viewpager\RecyclerView 滑动边缘阴影效果
    去除 viewpager 滑动到尽头时的阴影效果
    android:overScrollMode=“never”
    去除 RecyclerView 滑动到尽头时的阴影效果
    android:overScrollMode="never"
    android:scrollbars="none"
    在ViewPage2中设置android:overScrollMode="never"是没有用的,阴影动画依然存在,我们要在代码中这么设置

    View child = viewPager2.getChildAt(0);
    if (child instanceof RecyclerView) {
    child.setOverScrollMode(View.OVER_SCROLL_NEVER);
    }
    25、ProgressBar 个别机型 不动情况
    添加这个

      android:indeterminateDrawable="@drawable/anim_drawable_bg" 
    bg 内容
    <?xml version="1.0" encoding="utf-8"?>
    <animated-rotate xmlns:android="http://schemas.android.com/apk/res/android"
        android:drawable="@drawable/loading" 图片 静态就行
        android:fromDegrees="0.0"
        android:pivotX="50.0%"
        android:pivotY="50.0%"
        android:toDegrees="360.0" />
    

    26、Android字体加粗,UI小姐姐说太粗了,解决办法
    tv.getPaint().setStyle(Paint.Style.FILL_AND_STROKRE)
    tv.getPaint().setStrokeWidth(0.7)
    另外textStype="bold" 太粗

    27、TextView 和特殊符号在一起 显示不全等问题

    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.text.Layout;
    import android.text.StaticLayout;
    import android.text.TextPaint;
    import android.text.TextUtils;
    import android.util.AttributeSet;
    import android.widget.TextView;
    
    /**
     * ClassName:TypesetTextView
     * Date:     2014-11-29 上午10:10:31
     * @author Lenovo
     * @version
     * @since JDK 1.7
     * @see
     */
    public class TypesetTextView extends TextView {
    
    
        private int mLineY;
    
        private int mViewWidth;
    
        public static final String TWO_CHINESE_BLANK = "  ";
    
    
        private StringBuffer mText;
        private StringBuffer newText = null;
        private Paint mPaint;
        /**VIEW的高度*/
        private int mHeight = 0;
        /**行高*/
        private static final int LINE_HEIGHT = 40;
        private int oneLine;//一行显示文字个数
    
        private int number_of_words;//显示的字数
    
        public TypesetTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        @Override
        protected void onLayout(boolean changed, int left, int top, int right,
                                int bottom) {
            super.onLayout(changed, left, top, right, bottom);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
    
            String text = getText().toString();// 获取文本内容
            if (null == mText) {//以单例模式对文字进行拆分
                mText = new StringBuffer(text);
                TextPaint paint = getPaint();//获取画笔
                paint.setColor(getCurrentTextColor());// 获取文字颜色将其设置到画笔上
                paint.setTextSize(getTextSize());//设置文字大小
                paint.setTypeface(getTypeface());//设置字体,包括字体的类型,粗细,还有倾斜、颜色等
                paint.drawableState = getDrawableState();
                mViewWidth = getMeasuredWidth();//获取填写字数的宽
                mPaint = paint;
                caculateChangeLine();//对文字进行分行处理
            }
            mLineY = getPaddingTop();//设置头部内边距
            mLineY += getTextSize();
            Layout layout = getLayout();//避免出现空视图
    
            if (layout == null) {
                return;
            }
            Paint.FontMetrics fm = mPaint.getFontMetrics();
    
            int textHeight = (int) (Math.ceil(fm.descent - fm.ascent));
            textHeight = (int) (textHeight * layout.getSpacingMultiplier() + layout
                    .getSpacingAdd());//获取文字的高度
    
            String[] split = newText.toString().split("\n");//将分割好滴文字进行排版
            if (null != split && split.length > 0) {//此处设置文本显示的高度,适配一些手机无法显示
                int i = (split.length) + 0;//多设置了几行以避免显示不全(看情况进行修改)
                int setheight = textHeight * i;
                setHeight(setheight);//设置textview高度
            }
    
            for (int i = 0; i < split.length; i++) {
                //此处为源例子上的写法,标点符号换行问题还是存在(楼主引用,ToDBC(aaa)的方法进行了修改,已解决这个bug)
    //                layout.getLineCount()//获取显示的行数
    //                int lineStart = layout.getLineStart(i);
    //                int lineEnd = layout.getLineEnd(i);//获取每行要显示的字数
                String string = split[i];
                float width = StaticLayout.getDesiredWidth(string, 0,
                        string.length(), getPaint());
    
                if (null == string || TextUtils.isEmpty(string)) {
                    continue;
                }
                int strWidth = (int) mPaint.measureText(string + "好好");//验证是否足够一个屏幕的宽度
                if (needScale(string) && string.trim().length() > number_of_words - 5 && mViewWidth < strWidth)//判断是否足够一行显示的字数,足够久进行字的处理不够则直接画出来
                //,避免出现字数不够,字间距被画出来的字间距过大影响排版
                {// 判断是否结尾处需要换行,并且不是文本最后一行
                    drawScaledText(canvas, getPaddingLeft(), split[i], width, i);
                } else {
    
                    canvas.drawText(split[i], getPaddingLeft(), mLineY, mPaint);// 将字符串直接画到控件上
                }
                mLineY += textHeight;
            }
    
    
        }
    
        /**
         * @Description:计算出一行显示的文字
         */
        private String caculateOneLine(String str) {
            //对一段没有\n的文字进行换行
            String returnStr = "";
            int strWidth = (int) mPaint.measureText(str);
            int len = str.length();
            int lineNum = strWidth / mViewWidth; //大概知道分多少行
            int tempWidth = 0;
            String lineStr;
            int returnInt = 0;
            if (lineNum == 0) {
                returnStr = str;
                mHeight += LINE_HEIGHT;
                return returnStr;
            } else {
    
                oneLine = len / (lineNum + 1);    //一行大概有多少个字
                if (number_of_words < oneLine) {
                    number_of_words = oneLine;
                }
    
                lineStr = str.substring(0, oneLine);
                tempWidth = (int) mPaint.measureText(lineStr);
    
    
                if (tempWidth < mViewWidth) //如果小了 找到大的那个
                {
                    while (tempWidth < mViewWidth) {
                        oneLine++;
                        lineStr = str.substring(0, oneLine);
                        tempWidth = (int) mPaint.measureText(lineStr);
                    }
                    returnInt = oneLine - 1;
                    returnStr = lineStr.substring(0, lineStr.length() - 2);
                } else//大于宽找到小的
                {
                    while (tempWidth > mViewWidth) {
                        oneLine--;
                        lineStr = str.substring(0, oneLine);
                        tempWidth = (int) mPaint.measureText(lineStr);
                    }
                    returnStr = lineStr.substring(0, lineStr.length() - 1);
                    returnInt = oneLine;
                }
                mHeight += LINE_HEIGHT;
                returnStr += "\n" + caculateOneLine(str.substring(returnInt - 1));
            }
            return returnStr;
        }
    
        public void caculateChangeLine() {
            newText = new StringBuffer();
            String tempStr[] = mText.toString().split("\n");
            int len = tempStr.length;
            for (int i = 0; i < len; i++) {
                String caculateOneLine = caculateOneLine(tempStr[i]);
                if (!TextUtils.isEmpty(caculateOneLine)) {
                    newText.append(caculateOneLine);
                    newText.append("\n");
                }
    
            }
            this.setHeight(mHeight);
        }
    
    
        private void drawScaledText(Canvas canvas, int lineStart, String line,
                                    float lineWidth, int currentline) {
            float x = 0;
            if (isFirstLineOfParagraph(lineStart, line)) {// 判断是否是第一行
                canvas.drawText(TWO_CHINESE_BLANK, x, mLineY, getPaint());
                float bw = StaticLayout.getDesiredWidth(TWO_CHINESE_BLANK, getPaint());
                x += bw;
    
                line = line.substring(3);
            }
            int gapCount = line.length() - 1;
            int i = 0;
            if (line.length() > 2 && line.charAt(0) == 12288
                    && line.charAt(1) == 12288) {
                String substring = line.substring(0, 2);
                float cw = StaticLayout.getDesiredWidth(substring, getPaint());
                canvas.drawText(substring, x, mLineY, getPaint());
                x += cw;
                i += 2;
            }
    
            float d = (mViewWidth - lineWidth) / gapCount;
            for (; i < line.length(); i++) {
                String c = String.valueOf(line.charAt(i));
                float cw = StaticLayout.getDesiredWidth(c, getPaint());
                canvas.drawText(c, x, mLineY, getPaint());
                x += cw + d;
            }
        }
    
        private boolean isFirstLineOfParagraph(int lineStart, String line) {
            return line.length() > 3 && line.charAt(0) == ' '
                    && line.charAt(1) == ' ';
        }
    
        private boolean needScale(String line) {// 判断是否需要换行
            if (line == null || line.length() == 0) {
                return false;
            } else {
                char charAt = line.charAt(line.length() - 1);
                return charAt != '\n';
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:android 小技巧

        本文链接:https://www.haomeiwen.com/subject/huoijltx.html