Android 常见问题处理

作者: EitanLiu | 来源:发表于2016-10-24 13:29 被阅读601次

    总有那么些问题很常见又常忘,仅为了方便查看,主要介绍解决方法,原因不做过多说明,如有错误欢迎指正

    禁止ScrollView 内容改变自动滚动(获取焦点)

    修改布局文件方法

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/friend_scroll"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:orientation="vertical" >
            <!--......-->
        </LinearLayout>
    </ScrollView>
    

    代码获取scrollView的 LinearLayout 进行设置

    scrollView.getChildAt(0).setFocusable(ture);
    scrollView.getChildAt(0).setFocusableInTouchMode(ture);
    scrollView.getChildAt(0).requestFocus();
    
    

    问题一般发生在ScrollView内嵌ListView等一些会获取焦点的控件,本质问题仅仅是焦点被内部控件获取的,通过.smoothScrollTo(0, 0)在回到头部是不合理的

    EditView 内容监听参数解读

    Kotlin写法介绍, Java参考方法名注释

    class MyTextWatcher(position: Int, id: Int) :TextWatcher {
        override fun afterTextChanged(s: Editable?) {
            //editView.getSelectionStart() 起始选中焦点, getSelectionEnd() 结束选中焦点 , 监听事件的start为起始焦点
            //在TextWatcher中改变文本后需要重新设置焦点位置, 否则可能会焦点溢出崩溃 setSelection()
            if (holder.itemView.name.text.length > 5)
                holder.itemView.name.setSelection(0, 2)
            Log.e(this.javaClass.simpleName + "ss", "afterTextChanged-- ${s.toString()}, " +
                    "selectStart: ${holder.itemView.name.selectionStart}, selectEnd: ${holder.itemView.name.selectionEnd}, " +
                    "name: ${holder.itemView.name.text.length}")
        }
    
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            //s: 原始内容, start: 删除后(新增前)焦点 0开始, count: 删除数量, after: 新增数量
            Log.e(this.javaClass.simpleName + "ss", "beforeTextChanged-- start: $start,count: $count, after: $after, ${s.toString()}")
        }
    
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            //s: 改变结果, start: 删除后(新增前)焦点, before: 删除数量, count: 新增数量
            Log.e(this.javaClass.simpleName + "ss", "onTextChanged-- start: $start,before: $before, count: $count, ${s.toString()}")
        }
    
    })
    

    需要注意的是EditText每次调用addTextChangedListener(),这里是add不是set每次都会新增一个监听,不是替换掉原来的设置

    关闭AlertDialog

    AlertDialog.Builder 只有show()方法没有dismiss(),在create()或show()执行后会得到一个AlertDialog,即可执行dismiss()。了解设计模式一眼就可以看出是Builder创建模式,本不是问题,看到用模拟back返回键的来关闭方法,好吧真会玩。

    AlertDialog.Builder builder = newAlertDialog.Builder(this);
    AlertDialog dialog = builder.show();
    dialog.dismiss();
    
    

    AlertDialog 更改默认按钮样式

    AlertDialog有个getButton方法可以获取三个默认按钮,之后就可以进行修改字体颜色之类的操作了

    AlertDialog dialog = builder.create();
    Button yBtn = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
    Button nBtn = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
    yBtn.setTextColor(getResource().getColor(R.color.green));
    
    

    RecycleView 指定Item滚动到头部

    类似ListView.smoothScrollToPositionFromTop()方法
    Scroll RecyclerView to show selected item on top

    RecycleView 刷新错误

    1. 判断RecycleView是否处于计算或滚动
      !child_list.isComputingLayout() && (child_list.getScrollState() == RecyclerView.SCROLL_STATE_IDLE)

    2. Handle 异步处理, 或者在ViewHolder中使用同一个监听器

    问题一般发生RecycleAdapter在onBindViewHolder过程,为EditText添加TextWatcher和CheckBox设置OnCheckedChangeListener。当EditText内容或CheckBox状态改变,调用notifyDataSetChanged()刷新后,监听器发现内容又改变了,再次调用notifyDataSetChanged(),而此时RecycleView还处于计算布局状态,于是就报错了。即便不报错也会出现死循环,所以在RecycleView或ListView中慎用监听器。

    Button 取消全部大写

    android:textAllCaps="false"

    Android 5.0 Button 去阴影

    给Button添加stylestyle="?android:attr/borderlessButtonStyle"
    或者android:stateListAnimator="@null"

    borderlessButtonStyle 具体指向可以在源码的res目录下查看,在 themes_materialstyle/Widget.Material.Light.Button.Borderless

    添加默认点击效果

    点击效果在不同Android版本是不同的,5.0以上是新增ripple波纹效果,RelativeLayout等ViewGroup默认是没有点击效果的,一句话添加
    android:background="?android:attr/selectableItemBackground"

    删除ScrollView边界阴影方法方法

    (1) 在xml中添加:android:fadingEdge=”none”
    (2) 代码中添加:ScrollView.setHorizontalFadingEdgeEnabled(false);
    (3)2.3及以上的 否则不用设置: android:overScrollMode="never"

    模拟事件

    Runtime runtime = Runtime.getRuntime();
    //模拟返回键
    runtime.exec("input keyevent " + KeyEvent.KEYCODE_BACK);
    
    

    按键事件拦截

    返回false表示拦截

    //按下拦截
    @Override  
    public boolean onKeyDown(int keyCode, KeyEvent event)  
    {  
        
    }
    
    //松开拦截
    @Override  
    public boolean onKeyUp(int keyCode, KeyEvent event)  
    {  
        switch (keyCode){
            case KeyEvent.KEYCODE_BACK:
                if (isRegister) {
                    //自定义处理
                    return false;
                }
                break;
        }
        return super.onKeyUp(keyCode, event);
    }
    

    注:无法拦截返回键关闭输入法

    滚动条配置

    Android ListView滚动条配置完全解析

    TextView部分内容-设置高亮和点击

    https://my.oschina.net/u/2283209/blog/465583

        /**
         * 关键字高亮显示
         *
         * @param target 需要高亮的关键字
         * @param text   需要显示的文字
         * @return spannable 处理完后的结果,记得不要toString(),否则没有效果
         */
        public static SpannableStringBuilder highlight(String text, String target, final View.OnClickListener listener) {
            SpannableStringBuilder spannable = new SpannableStringBuilder(text);
            CharacterStyle span = null;
    
            Pattern p = Pattern.compile(target);
            Matcher m = p.matcher(text);
            while (m.find()) {
                //span = new ForegroundColorSpan(Color.RED);// 需要重复!
                span = new ClickableSpan() {
                    @Override
                    public void updateDrawState(TextPaint ds) {
                        //高亮颜色
                        ds.setColor(ds.linkColor);
                        //取消下滑线
                        ds.setUnderlineText(false);
                    }
    
                    @Override
                    public void onClick(View widget) {
                        if (listener != null)
                            listener.onClick(widget);
                    }
                };
                spannable.setSpan(span, m.start(), m.end(),
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
            return spannable;
        }
    
        //shop_treaty_tv.setText(text, BufferType.SPANNABLE);
        //改变高亮颜色
        shop_treaty_tv.setLinkTextColor(0xFFD8271C);
        //设置高亮和点击事件
        shop_treaty_tv.setText(highlight(shop_treaty_tv.getText().toString(), "《协议内容》", listenter));
        shop_treaty_tv.setMovementMethod(LinkMovementMethod.getInstance());
    

    输入法相关

    设置android:windowFullscreen后android:windowSoftInputMode的adjustResizeadjustPan将无效过

    监听输入法状态

    //输入模式
    Log.e("softMode", "-" + getWindow().getAttributes().softInputMode);
    //当前窗口是焦点
    Log.e("focus", "-" + getCurrentFocus());
    //输入法状态一直为true
    Log.e("focus", "-" + inputMethodManager.isActive());
    //EditView是输入的焦点
    Log.e("focus", "-" + inputMethodManager.isActive(binding.mobileEdt));
    //存在一个EditView有输入焦点
    Log.e("focus", "-" + inputMethodManager.isAcceptingText());
    

    ResultReceiver监听调用结果,设置Hander的作用仅为切换回调线程

        new ResultReceiver(null){
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                super.onReceiveResult(resultCode, resultData);
                switch (resultCode){
                    case InputMethodManager.RESULT_UNCHANGED_SHOWN:
                        break;
                    case InputMethodManager.RESULT_UNCHANGED_HIDDEN:
                        break;
                    case InputMethodManager.RESULT_SHOWN:
                        //成功显示键盘回调
                        break;
                    case InputMethodManager.RESULT_HIDDEN:
                        //成功隐藏键盘回调
                        break;
                }
                Log.e("ResultReceiver", "-" + resultCode);
                Log.e("ResultReceiver", "-" + resultData);
            }
        }
    

    显示输入法

        //显示软件盘
        protected boolean showSoftKeyboard(View view){
            final boolean[] isShow = {false};
            if (view != null){
                inputMethodManager.showSoftInput(view,
                        InputMethodManager.SHOW_FORCED, new ResultReceiver(null) {
                            @Override
                            protected void onReceiveResult(int resultCode, Bundle resultData) {
                                super.onReceiveResult(resultCode, resultData);
                                if (resultCode == InputMethodManager.RESULT_SHOWN) {
                                    //成功隐藏键盘回调
                                    isShow[0] = true;
                                }
                                //Log.e("ResultReceiver", "-" + resultCode);
                                //Log.e("ResultReceiver", "-" + resultData);
                            }
                        }
                );
            }
            return isShow[0];
        }
    
    

    隐藏输入法

        //隐藏软键盘
        protected boolean hideSoftKeyboard() {
            final boolean[] isHide = {false};
            if (getCurrentFocus() != null) {
                inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(),
                        InputMethodManager.HIDE_NOT_ALWAYS, new ResultReceiver(null) {
                            @Override
                            protected void onReceiveResult(int resultCode, Bundle resultData) {
                                super.onReceiveResult(resultCode, resultData);
                                if (resultCode == InputMethodManager.RESULT_HIDDEN) {
                                    //成功隐藏键盘回调
                                    isHide[0] = true;
                                }
                                //Log.e("ResultReceiver", "-" + resultCode);
                                //Log.e("ResultReceiver", "-" + resultData);
                            }
                        }
                );
            }
            return isHide[0];
        }
    

    切换显示隐藏

    inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
    
    

    Android InputMethodManager输入法简介

    获取状态栏高度

    // 获取状态栏高度
    public static int getStatusBarHeight(Context context) {
        try {
            Class<?> c = Class.forName("com.android.internal.R$dimen");
            Object obj = c.newInstance();
            Field field = c.getField("status_bar_height");
            int x = Integer.parseInt(field.get(obj).toString());
            return context.getResources().getDimensionPixelSize(x);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
    

    Fragment 显示隐藏监听

    分为三中情况,切换到其他Activity,FragmentTransaction切换,ViewPager中切换;
    1). Activity 切换可在生命周期onResume,onPause中监听
    2). FragmentTransaction 切换重写onHiddenChanged判断。
    3). ViewPager 切换重写setUserVisibleHint 判断。

    CoordinatorLayout RecycleView(NestedScrollView)滑动到顶部

    Programmatically scroll to the top of a NestedScrollView

    如何监听Activity生命周期

    系统并没有直接监听Activity 生命周期的方法,那么换个思路新建一个没有视图的Fragment,添加Fragment到Activity ,监听Fragment的生命周期即可。

    读取资源文件

    文件类型 File、FileDescriptor、ParcelFileDescriptor
    Returning an Input Stream from Parcel File Descriptor using Androids DownloadManager
    how to get ParcelFileDescriptor for file content?
    java io系列09之 FileDescriptor总结

    ContentResolver contentResolver = context.getContentResolver();
    // file:///android_asset/
    // android.resource://package_name/type/resource_id
    // Android.resource://package_name/type/resource_name
    // eg. "android.resource://package_name/raw/" + R.raw.music
    InputStream is = contentResolver.openInputStream(uri);
    
    ParcelFileDescriptor rdesc = contentResolver.openAssetFileDescriptor(uri, "r").getParcelFileDescriptor();
    ParcelFileDescriptor wdesc = contentResolver.openFileDescriptor(uri, "w");
    

    渲染方式(硬件加速)

    view.setLayerType(View.LAYER_TYPE_NONE, mPaint); //默认渲染
    view.setLayerType(View.LAYER_TYPE_SOFTWARE, mPaint); //软件渲染
    view.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); //硬件加速
    

    xml配置

    android:layerType="software"
    

    本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。转载请保留作者及原文链接

    相关文章

      网友评论

        本文标题:Android 常见问题处理

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