Android开发踩坑汇总2018.7月


EditText自动获取焦点

在一个有很多EditText的界面,进入会自动获取焦点。而我们不需要这种行为,解决:

在父容器中拦截掉事件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/dialog_layout_464_wrap"
    android:orientation="vertical"
    android:focusable="true"
    android:focusableInTouchMode="true"
    android:paddingTop="@dimen/di_10px">

或者定义在style文件中:

<style name="dialog_week_rl">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:paddingLeft">@dimen/di_106px</item>
    <item name="android:paddingRight">@dimen/di_73px</item>
    <!--使其默认不被光标选中-->
    <item name="android:focusable">true</item>
    <item name="android:focusableInTouchMode">true</item>
</style>

判断点击的位置是否在一个圆的某个象限中:

思路:先计算点击的位置和圆心与水平线所成的夹角,然后通过角度判断即可。

计算夹角:

/**
 * 根据触摸的位置,计算角度
 *
 * @param x
 * @param y
 * @return
 */private double getAngle(float x, float y) {
    double angle = Math.atan2(y - mCenterY, x - mCenterX);
    return -angle * 180 / 3.14;
}

View事件传递机制中true与false含义:

不知为何总得试两次,onTouch中返回false,则表示不拦截当前事件,事件可以正常传递,onClick可以顺利执行。反之,事件被消费。

在ViewGroup中的onInterceptTouchEvent返回false,则不拦截点击事件,事件可正常传递。反之,事件被消费。

在ViewGroup中的onTouchEvent返回true表示拦截,否则不拦截。

执行顺序:

dispatchTouchEvent > onTouch > onTouchEvent > onClick

主要方法:

1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
2)public boolean onInterceptTouchEvent(MotionEvent ev) 这个方法用来拦截TouchEvent
3)public boolean onTouchEvent(MotionEvent ev) 这个方法用来处理TouchEvent

事件传递原理解释:

​当TouchEvent发生时(一次点击),首先Activity将TouchEvent传递给最顶层的View,TouchEvent最先到达最顶层 view 的 dispatchTouchEvent ,然后由 dispatchTouchEvent 方法进行分发,如果dispatchTouchEvent返回true ,则交给这个view的onTouchEvent处理,如果dispatchTouchEvent返回 false ,则交给这个 view 的 interceptTouchEvent 方法来决定是否要拦截这个事件,如果 interceptTouchEvent 返回 true ,也就是拦截掉了,则交给它的 onTouchEvent 来处理,如果 interceptTouchEvent 返回 false ,那么就传递给子 view ,由子 view 的 dispatchTouchEvent 再来开始这个事件的分发。如果事件传递到某一层的子 view 的 onTouchEvent 上了,这个方法返回了 false ,那么这个事件会从这个 view 往上传递,都是 onTouchEvent 来接收。而如果传递到最上面的 onTouchEvent 也返回 false 的话,这个事件就会“消失”, 而且接收不到下一次事件。(我说的一次事件指的是 down 到 up 之间的一系列事件)。Touchevent 中,返回值是 true ,则说明消耗掉了这个事件。​

参考:https://blog.csdn.net/yan943789510/article/details/47948413

TextView原生跑马灯:

给TextView加上属性:

android:singleLine="true"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"

TextView宽度需要限制,match_parent或者设置maxLength,因为跑马灯需要一直获取焦点,所以原生是无法两个view同时跑的。

代码中设置Margin

padding很好设置:

//设置view的padding属性
view.setPadding(left, top, right, bottom);

margin需要转个弯:

/**
 * 设置某个View的margin
 *
 * @param view   需要设置的view
 * @param isDp   需要设置的数值是否为DP
 * @param left   左边距
 * @param right  右边距
 * @param top    上边距
 * @param bottom 下边距
 * @return
 */public static ViewGroup.LayoutParams setViewMargin(View view, boolean isDp, int left, int right, int top, int bottom) {
    if (view == null) {
        return null;
    }

    int leftPx = left;
    int rightPx = right;
    int topPx = top;
    int bottomPx = bottom;
    ViewGroup.LayoutParams params = view.getLayoutParams();
    ViewGroup.MarginLayoutParams marginParams = null;
    //获取view的margin设置参数
    if (params instanceof ViewGroup.MarginLayoutParams) {
        marginParams = (ViewGroup.MarginLayoutParams) params;
    } else {
        //不存在时创建一个新的参数
        marginParams = new ViewGroup.MarginLayoutParams(params);
    }

    //根据DP与PX转换计算值
    if (isDp) {
        leftPx = getPxFromDpi(left);
        rightPx = getPxFromDpi(right);
        topPx = getPxFromDpi(top);
        bottomPx = getPxFromDpi(bottom);
    }
    //设置margin
    marginParams.setMargins(leftPx, topPx, rightPx, bottomPx);
    view.setLayoutParams(marginParams);
    return marginParams;
}

使View绕点旋转:

Animation mAnimationRoate;
mAnimationRoate = new RotateAnimation(0.0f, 360, Animation.ABSOLUTE, 300, Animation.ABSOLUTE, 500);
mAnimationRoate.setDuration(1000 * 60);
//动画完成后不恢复原状
mAnimationRoate.setFillAfter(true);
mAnimationRoate.setRepeatCount(Animation.INFINITE);
mAnimationRoate.setRepeatMode(Animation.REVERSE);
imageView.startAnimation(mAnimationRoate);

0-360度 300,500为圆心的x,y

时长60秒,这里的模式为:Animation.ABSOLUTE绝对模式

模式相关:https://blog.csdn.net/zly921112/article/details/51476856

将图片转为BitMap:

mBitmap = ((BitmapDrawable)getContext().getResources().getDrawable(R.drawable.icon)).getBitmap();

android 8.0使用悬浮窗时,报错permission denied for window type 2007

因为android O对悬浮窗的设计做了一些修改,在使用android.permission.SYSTEM_ALERT_WINDOW 权限的应用无法再使用以下窗口类型来在其他应用和系统窗口上显示悬浮窗:

WindowManager.LayoutParams.TYPE_PRIORITY_PHONE
WindowManager.LayoutParams.TYPE_PHONE
WindowManager.LayoutParams.TYPE_SYSTEM_ERROR
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY

解决办法:

在android O及以上版本中,google为我们新增了WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 的新窗口类型来实现悬浮窗的效果,其他地方和以前版本一样。使用WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 窗口类型时,需要记住新窗口类型的以下特性:

应用的悬浮窗始终显示在状态栏和输入法等关键系统窗口的下面
系统可以移动使用WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 窗口类型的悬浮窗或者调整其大小,以改善屏幕显示效果
通过打开通知栏,用户可以访问设置来阻止应用显示使用WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 窗口类型的悬浮窗
总之,在android O以及以上版本要是用悬浮窗功能,你需要把悬浮窗的type设置成WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 就可以了

来源:https://blog.csdn.net/LoveDou0816/article/details/79172637

Android绘图Paint.setAntiAlias()和Paint.setDither()方法的作用

Paint.setAntiAlias() 抗锯齿
Paint.setDither() 防抖动

系统语言信息获取

Locale locale = context.getResources().getConfiguration().locale;

监听Wifi状态:

权限:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

广播:

WifiManager.WIFI_STATE_CHANGED_ACTION

其余:

private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null) {
                return;
            }
            String action = intent.getAction();
            if (TextUtils.isEmpty(action)) {
                return;
            }
            Log.d("NetInfo", "onReceive: "+action);
            switch (action) {
                case WifiManager.RSSI_CHANGED_ACTION:
//                    wifi信号强度变化
                    return;
                case WifiManager.NETWORK_STATE_CHANGED_ACTION:
                    NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                    switch (info.getState()) {
                        case DISCONNECTED:
//                            wifi断开
                            break;
                        case CONNECTED:
//                           wifi连接
                            break;
                        default:
                            break;
                    }
                    break;
                case WifiManager.WIFI_STATE_CHANGED_ACTION:
                    int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
                    switch (wifiState) {
                        case WifiManager.WIFI_STATE_DISABLED:
//                            系统关闭wifi
                            break;
                        case WifiManager.WIFI_STATE_ENABLED:
//                            系统开启wifi
                            break;
                        default:
                            break;
                    }
                    break;
                case ConnectivityManager.CONNECTIVITY_ACTION:
                    //获取联网状态的NetworkInfo对象
                    NetworkInfo infos = intent
                            .getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
                    if (infos != null) {
                        //如果当前的网络连接成功并且网络连接可用
                    }
                    break;
                default:
                    break;
            }
        }
    };

启动其他应用的service/Activity

Activity:

Intent intent = new Intent(Intent.ACTION_MAIN);
ComponentName componentName = new ComponentName("packageName", "packageName.Settings");
intent.setComponent(componentName);
startActivity(intent);

Service:

Intent intent = new Intent( );
ComponentName componentName = new ComponentName("packageName", "packageName.MyService");
intent.setComponent(componentName);
startService(intent);

使系统自带DatePicker、TimePicker、NumberPicker控件不可编辑

代码中:

mDatePicker.setDescendantFocusability(DatePicker.FOCUS_BLOCK_DESCENDANTS);

xml中:

android:descendantFocusability="blocksDescendants"

LayoutInflater获取方式:

activity中:

LayoutInflater inflater = getLayoutInflater();

通过context:

LayoutInflater inflater = LayoutInflater.from(mContext);

最终调用方法(前两个都是调用的这个):

LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

去除Activity设置为Dialog主题时的边框边距

使用下面的主题即可:

<resources>
    <style name="dialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:background">@android:color/black</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:backgroundDimEnabled">true</item>
    </style>
</resources>

 

声明:TIL|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA[ZH]协议进行授权

转载:转载请注明原文链接 - Android开发踩坑汇总2018.7月


Life is very interesting. In the end, some of your greatest pains become your greatest strengths.