Android 杂技

图片之Exif信息设置

ExifInterface exif = new ExifInterface(filePath.getAbsolutePath());
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE, GPS.convert(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LATITUDE_REF, GPS.latitudeRef(latitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE, GPS.convert(longitude));
exif.setAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF, GPS.longitudeRef(longitude));
exif.saveAttributes();

/*
 * @author fabien
 */
public class GPS {
    private static StringBuilder sb = new StringBuilder(20);

    /**
     * returns ref for latitude which is S or N.
     * @param latitude
     * @return S or N
     */
    public static String latitudeRef(double latitude) {
        return latitude<0.0d?"S":"N";
    }

    /**
     * returns ref for latitude which is S or N.
     * @param latitude
     * @return S or N
     */
    public static String longitudeRef(double longitude) {
        return longitude<0.0d?"W":"E";
    }

    /**
     * convert latitude into DMS (degree minute second) format. For instance<br/>
     * -79.948862 becomes<br/>
     *  79/1,56/1,55903/1000<br/>
     * It works for latitude and longitude<br/>
     * @param latitude could be longitude.
     * @return
     */
    synchronized public static final String convert(double latitude) {
        latitude=Math.abs(latitude);
        int degree = (int) latitude;
        latitude *= 60;
        latitude -= (degree * 60.0d);
        int minute = (int) latitude;
        latitude *= 60;
        latitude -= (minute * 60.0d);
        int second = (int) (latitude*1000.0d);

        sb.setLength(0);
        sb.append(degree);
        sb.append("/1,");
        sb.append(minute);
        sb.append("/1,");
        sb.append(second);
        sb.append("/1000");
        return sb.toString();
    }
}

如何在Android上的exif数据中保存GPS坐标?

Render process (15791) kill (OOM or update) wasn’t handed by all associated webviews, killing application.

关闭硬件加速
android:hardwareAccelerated=“false”

H5 混合开发,软键盘挡住输入框问题的终极解决方案

https://www.jianshu.com/p/306482e17080

AndroidBug5497Workaround

public class AndroidBug5497Workaround {

    // For more information, see https://issuetracker.google.com/issues/36911528
    // To use this class, simply invoke assistActivity() on an Activity that already has its content view set.

    public static void assistActivity (Activity activity) {
        new AndroidBug5497Workaround(activity);
    }

    private View mChildOfContent;
    private int usableHeightPrevious;
    private FrameLayout.LayoutParams frameLayoutParams;

    private AndroidBug5497Workaround(Activity activity) {
        FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
        mChildOfContent = content.getChildAt(0);
        mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            public void onGlobalLayout() {
                possiblyResizeChildOfContent();
            }
        });
        frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
    }

    private void possiblyResizeChildOfContent() {
        int usableHeightNow = computeUsableHeight();
        if (usableHeightNow != usableHeightPrevious) {
            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
            if (heightDifference > (usableHeightSansKeyboard/4)) {
                // keyboard probably just became visible
                frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
            } else {
                // keyboard probably just became hidden
                frameLayoutParams.height = usableHeightSansKeyboard;
            }
            mChildOfContent.requestLayout();
            usableHeightPrevious = usableHeightNow;
        }
    }

    private int computeUsableHeight() {
        Rect r = new Rect();
        mChildOfContent.getWindowVisibleDisplayFrame(r);
        return (r.bottom - r.top);
    }
}

Android Studio中不能展开jdk源码,无法import包

修改.idea->misc.xml,将project-jdk-name="JDK"改为对应的版本名字,如:11(Project Structure中配置的gradle jdk版本)

<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
  <component name="DesignSurface">
    <option name="filePathToZoomLevelMap">
      <map>
        <entry key="..\:/work/AndroidProject/DEMO/app/src/main/res/layout/activity_main.xml" value="0.20364583333333333" />
      </map>
    </option>
  </component>
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="JDK" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  <component name="ProjectType">
    <option name="id" value="Android" />
  </component>
</project>

改为

  ...
 <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="11" project-jdk-type="JavaSDK">
    <output url="file://$PROJECT_DIR$/build/classes" />
  </component>
  ...

原帖

Android Studio实现透明状态栏

    //是否使用特殊的标题栏背景颜色,android5.0以上可以设置状态栏背景色,如果不使用则使用透明色值
    protected boolean useThemestatusBarColor = true;
    //是否使用状态栏文字和图标为暗色,如果状态栏采用了白色系,则需要使状态栏和图标为暗色,android6.0以上可以设置
    protected boolean useStatusBarColor = true;

    protected void setStatusBar() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { //5.0及以上
            Window window = getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
            //根据上面设置是否对状态栏单独设置颜色
            if (useThemestatusBarColor) {
                window.setStatusBarColor(getResources().getColor(R.color.blue));//设置状态栏背景色
            } else {
                window.setStatusBarColor(Color.TRANSPARENT);//透明
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && useStatusBarColor) {//android6.0以后可以对状态栏文字颜色和图标进行修改
            getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        }
    }

原帖

再学一遍android:fitsSystemWindows属性
Android关于StatusBar(状态栏)总结

ARouter报错Manifest merger failed

需要在项目的gradle.properties中添加

android.useAndroidX=true
android.enableJetifier=true
  • android.useAndroidX=true
    表示“Android插件会使用对应的AndroidX库,而非Support库”;未设置时默认为false;
  • android.enableJetifier=true
    表示Android插件会通过重写其二进制文件来自动迁移现有的第三方库,以使用AndroidX依赖项;未设置时默认为false

android添加依赖ARouter报错Manifest merger failed

gradle中配置android.useAndroidX与android.enableJetifier使应用对support库的依赖自动转换为androidx的依赖

Android矢量图

Android矢量图(三)–VectorDrawable渐变色

onNewIntent 的调用时机

不是每次都会调用onRestart()

Android activity onNewIntent 调用时机

兼容性registerForActivityResult替代onActivityResult

How to Select Files in Android Easily? Connecting Storage Access Framework and Activity Result API

Android Studio 报错 Invalid Gradle JDK configuration found.

将.idea路径下的gradle.xml文件删除,再sync
参考

使用buildSrc目录提取gradle配置变量

object Apps {
    const val compileSdk: Int = 30
    const val minSdk = 21
    const val targetSdk = 30
//    const val versionCode = 1
//    const val versionName = "1.0.0"
}

报错

问题:报 Overload resolution ambiguity错误
解决:删除掉buildSrc目录下的.gradle和build目录,再gradle编译生成新的
删除圈起的文件夹

Android业务组件化之URL Scheme使用

链接:https://www.jianshu.com/p/da801097c79d

Android使用MediaStore获取手机上的文件

链接:https://blog.youkuaiyun.com/yann02/article/details/92844364

彻底搞懂Android文件存储—内部存储,外部存储以及各种存储路径解惑

链接:https://blog.youkuaiyun.com/u010937230/article/details/73303034

AndroidStudio Build Output 日志乱码

D:\work\AndroidProject\xx\app\build\generated\ap_generated_sources\devDebug\out\com\xx\DataBinderMapperImpl.java:9: ����: �Ҳ�������
import com.xx.databinding.FragmentMeBindingImpl;
                                      ^
  ����:   �� FragmentMeBindingImpl
  �: ����� com.xx.databinding

方法:Help->Edit Custom VM Options… 打开 studio64.exe.vmoptions 文件,加入-Dfile.encoding=UTF-8,重启即可

# custom Android Studio VM options, see https://developer.android.com/studio/intro/studio-config.html
-Dfile.encoding=UTF-8
D:\work\AndroidProject\xx\app\build\generated\ap_generated_sources\devDebug\out\com\xx\DataBinderMapperImpl.java:9: 错误: 找不到符号
import com.xx.databinding.FragmentMeBindingImpl;
                                      ^
  符号:   类 FragmentMeBindingImpl
  位置: 程序包 com.xx.databinding

参考

Android相关路径自定义

1. Android Studio配置:idea.properties文件

 idea.config.path=D:/Android/.AndroidStudio/config
 idea.system.path=D:/Android/.AndroidStudio/system
 idea.plugins.path=${idea.config.path}/plugins
 idea.log.path=${idea.system.path}/log

idea.properties截图

2. ANDROID_HOME

Android的SDK位置 例如:D:\Android\sdk

也可用以下操作修改SDK路径:Appearance & Behavior → System Settings → Android SDK中,点击 Android SDK Location → Edit
在这里插入图片描述

3. ANDROID_AVD_HOME

Android 虚拟机的保存位置,例如:D:\Android\avd
在这里插入图片描述

4. GRADLE_USER_HOME

gradle仓库存放路径,例如:D:\Android.gradle
在这里插入图片描述

Android Q分区存储

关于Android Q分区存储的一些适配心得

Android10填坑适配指南,实际经验代码,拒绝翻译

开源中国客户端 Android Q 经验适配指南,含代码

Android-Q适配-存储方式

java的四舍五入

目前java支持7中舍入法:

  1. ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。
  2. ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。
  3. ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。
  4. ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。
  5. HALF_UP:最近数字舍入(5进)。这是我们最经典的四舍五入。
  6. HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。
  7. HAIL_EVEN:银行家舍入法。

方法一:四舍五入

double f = 111231.5585;
BigDecimal b = new BigDecimal(f);
double f1 = b.setScale(2, RoundingMode.HALF_UP).doubleValue();
// 在这里使用BigDecimal ,并且采用setScale方法来设置精确度,
//同时使用RoundingMode.HALF_UP表示使用最近数字舍入法则来近似计算。

方式二:

new java.text.DecimalFormat("#.00").format(3.1415926)
// #.00 表示两位小数 #.0000四位小数 以此类推…

方式三:

double d = 3.1415926;
String result = String .format("%.2f");
// %.2f %. 表示 小数点前任意位数   2 表示两位小数 格式后的结果为f 表示浮点型。

引自 https://www.cnblogs.com/chenssy/p/3366632.html

手势操作onScroll没有执行(以及onFling问题)

现象1

将某一手势操作交给GestureDetector时,GestureDetector重写了几个方法,但是onScroll没有执行,其他的onDOwn,OnLongPress都有执行。

解决:onDOwn方法的返回值返回true

现象2

虽然此时onScroll(滑动)方法调用了,但是打印结果表示:该方法会调用多次,有时并不是我所需要的,而onFling表示滑动,当手离开时调用,但是打印的结果显示并没有调用;

解决:在之前的基础上再将onScroll返回false即可!

原文链接:https://blog.youkuaiyun.com/flycatdeng/article/details/18267133

ImageSpan

// 第一种
Drawable drawable = getResources().getDrawable(R.drawable.ic_coin);
drawable.setBounds(0,0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE);
// 第一种
ImageSpan imageSpan = new ImageSpan(mContext, R.drawable.ic_coin);

stringBuilder.setSpan(imageSpan, size-1, size, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

FragmentPagerAdapter与FragmentStatePagerAdapter区别

https://www.cnblogs.com/lianghui66/p/3607091.html

判断 RecyclerView 到达底部的方法

第1种
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {

            private boolean isToBottom = false;

            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE && isToBottom) {

                    loadMore();
                }
            }

            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                isToBottom = isSlideToBottom(recyclerView);
                swipeRefresh.setEnabled(isSlideToTop(recyclerView));
            }
        });
        
	/**
     * 判断是否滑动到底部
     */
    public static boolean isSlideToBottom(RecyclerView recyclerView) {
        if (recyclerView == null) return false;
        return recyclerView.computeVerticalScrollExtent() + recyclerView.computeVerticalScrollOffset()
                >= recyclerView.computeVerticalScrollRange();
    }

    /**
     * 判断是否滑动到顶部
     */
    public static boolean isSlideToTop(RecyclerView recyclerView) {

        if (recyclerView == null) return false;
        return recyclerView.computeVerticalScrollOffset() == 0;
    }
第2种
	/**
     * 判断是否滑动到底部
     */
    public static boolean isSlideToBottom(RecyclerView recyclerView) {
       return !recyclerView.canScrollVertically(1); //表示是否能向上滚动,false 表示已经滚动到底部 
    }

    /**
     * 判断是否滑动到顶部
     */
    public static boolean isSlideToTop(RecyclerView recyclerView) {
        return !recyclerView.canScrollVertically(-1); // 表示是否能向下滚动,false 表示已经滚动到顶部
    }
第3种
	RecyclerView rvMessage = findViewById(R.id.rv_message);

        GridLayoutManager layoutManager = new GridLayoutManager(this, 1, RecyclerView.VERTICAL, false);
        rvMessage.setLayoutManager(layoutManager);
        MessageAdapter messageAdapter = new MessageAdapter();
        rvMessage.setAdapter(messageAdapter);

        rvMessage.addOnScrollListener(new RecyclerView.OnScrollListener() {

            private int lastVisible = 0;

            @Override
            public void onScrollStateChanged([@NonNull](https://my.oschina.net/u/2981441) RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE &&
                        lastVisible + 1 == messageAdapter.getItemCount()) {

                    // 已滑动到底部
                }
            }

            [@Override](https://my.oschina.net/u/1162528)
            public void onScrolled([@NonNull](https://my.oschina.net/u/2981441) RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                lastVisible = layoutManager.findLastCompletelyVisibleItemPosition();
            }
        });

参考

http://quanzhan.applemei.com/webStack/TVRrNE9RPT0=

将十六进制 颜色代码 转换为int类型数值

Color.parseColor(“#FF00FF”)

getColor在6.0中过时

MainActivity.this.getResources().getColor(R.color.colorTest)

用以下替换

ContextCompat.getColor(context, R.color.my_color)

//源码 
public static final int getColor(Context context, int id)
{
final int version = Build.VERSION.SDK_INT;
if (version >= 23) {
	return ContextCompatApi23.getColor(context, id);       
} else {
	return context.getResources().getColor(id);
	}
}

SparseArray

SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch)

Listview的Adapter的转换

如果ListView没有headerView或者footerView的时候,与listView相关联的Adapter就是传进来的参数Adapter,如果有,就将原来的Adapter包装成HeaderViewListAdapter,HeaderViewListAdapter有个方法getWrappedAdapter,该方法能返回被包装的HeaderViewListAdapter的ListAdapter。

HeaderViewListAdapter headerViewListAdapter = (HeaderViewListAdapter) listView.getAdapter(); 
ContactAdapter contactAdapter = (ContactAdapter) headerViewListAdapter.getWrappedAdapter();

使用View做虚线分割线

drawable/line_dash.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="line">
    <stroke android:color="@color/colorBlack"
            android:dashGap="2dp"
            android:dashWidth="5dp"
            android:width="1dp" />
    <size android:width="1dp" />
</shape>

布局文件中:(需要设置 layerType为software)

<View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="@drawable/line_dash"
        android:layerType="software"
        />

Android api23 删除HttpClient 相关类

在Module的gradle.build文件添加依赖,和 packagingOptions,和 useLibrary ‘org.apache.http.legacy’

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.egrid.webdemo"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

     packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE.txt'
    }
    //会加载Andrdoid自带的jar包 org.apache.http.legacy.jar
     useLibrary 'org.apache.http.legacy'
}


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    //下面这些不会起作用
    compile 'org.apache.httpcomponents:httpclient:4.3.6'
    compile 'org.apache.httpcomponents:httpcore:4.3.2'
}

在xml中使用系统的actionBarSize

  1. 在attrs.xml文件中定义
<resources>
    <declare-styleable name="app">
        <attr name="toolbarHeight" format="dimension" />
    </declare-styleable>
</resources>
  1. 在styles.xml中自定义toolbarHeight为actionBarSize
 <style name="BaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
        <item name="colorButtonNormal">@color/blueLight</item>
        <item name="colorControlHighlight">@color/background</item>
        <item name="toolbarHeight">?android:attr/actionBarSize</item>
    </style>

  1. 在布局xml中使用
 <androidx.appcompat.widget.Toolbar
	 android:id="@+id/toolbar"
	 android:layout_width="match_parent"
	 android:layout_height="?attr/toolbarHeight"
	 app:contentInsetStart="0dp"
	 app:layout_collapseMode="pin" />

Splash 的最佳实践

此方法是给主界面设置闪屏页主题,在主界面中再用代码换回主界面主题,实现无闪屏Activity达到闪屏页的效果。

  1. 闪屏主题
<resources>
  <!-- 主界面主题 -->
  <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
  </style>
   <!-- 闪屏主题 -->
  <style name="AppTheme.Launcher">
    <item name="android:windowBackground">@drawable/launch_screen</item>
  </style>
<resources>
  1. 闪屏界面背景drawable/launch_screen.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque">
    <item android:drawable="@color/white" />
     <bitmap
            android:gravity="center"
            android:src="@drawable/img_splash" />
</layer-list>
  1. 主界面设置主题
<activity
 android:name=".ui.activity.MainActivity"
 android:theme="@style/AppTheme.Launcher">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
  1. 主界面super.onCreate() 之前调用 onCall() 方法设置回应用主题
public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceStated) {
    setTheme(R.style.AppTheme);
    super.onCreate(savedInstanceStated);
    // ...
  }
}

注:若有闪屏Activity可以直接使用主题设置闪屏背景,无需在布局设置背景,所以没有背景显示时的切换问题

链接:https://blog.youkuaiyun.com/zujack/article/details/69499350

TextView 设置和取消删除线的两种方法

设置删除线
  1. (推荐)通过按位或运算符|,将 TextView 原本的 Flags 属性和删除线一块设置。setPaintFlags内会对 TextView 进行重绘。
tv.setPaintFlags(tv.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
  1. 获取画笔后设置属性,重绘 TextView 。此方式有个问题,会把 TextView 原本的 Flags 属性替代,例如抗锯齿等。仔细查看,你会发现通过这种方式,文字有了锯齿。
tv.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
tv.invalidate();
取消删除线
  1. (推荐)先对 Paint.STRIKE_THRU_TEXT_FLAG 属性取反,再用按位与运算符&,除去了删除线属性并保留了 TextView 原本的 Flags 属性。setPaintFlags内会对 TextView 进行重绘。
tv.setPaintFlags(tv.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
  1. 获取画笔后,清空 Flags 属性,再重绘 TextView 。此方式有个问题,会把 TextView 原本的所有 Flags 属性清空,例如抗锯齿等。仔细查看,你会发现通过这种方式,文字有了锯齿;
tv.getPaint().setFlags(0);
tv.invalidate();

参考:https://www.jb51.net/article/136661.htm

RecyclerView滚动到指定位置并置顶

  • https://blog.youkuaiyun.com/weimingjue/article/details/82805361
 // 自定义类
 public class TopSmoothScroller extends LinearSmoothScroller {

    public TopSmoothScroller(Context context) {
        super(context);
    }

    @Override
    protected int getHorizontalSnapPreference() {
        return SNAP_TO_START;//具体见源码注释
    }

    @Override
    protected int getVerticalSnapPreference() {
        return SNAP_TO_START;//具体见源码注释
    }
}

// 使用
 LinearSmoothScroller scroller = new TopSmoothScroller(CourseActivity.this);
 scroller.setTargetPosition(position);
 linearLayoutManager.startSmoothScroll(scroller);

用shape画的虚线在真机上无法显示

在布局View添加属性 android:layerType=“software”

安装debug包时手机(OPPO VIVO)显示INSTALL_FAILED_TEST_ONLY无法安装

Android Studio 3.0以后会在debug apk的manifest文件中加android:testOnly=”true” 属性,此属性无法在非开发者模式的手机上安装,VIVO OPPO即使在开发者模式也不能安装。

解决: 在gradle.properties文件中添加android.injected.testOnly=false

沉浸式模式

Android关于沉浸式状态栏总结

https://juejin.im/post/5989ded56fb9a03c3b6c8bde

Android状态栏微技巧,带你真正理解沉浸式模式

https://blog.youkuaiyun.com/guolin_blog/article/details/51763825

Android 沉浸式状态栏必知必会

https://juejin.im/entry/58cb481744d9040069f57266

刘海屏适配cutout

刘海屏适配 与WindowInsets,DisplayCutout使用

https://blog.youkuaiyun.com/yi_master/article/details/80309757

Android Studio配置文件路径修改

.AndroidStudio文件夹修改

修改文件C:\Program Files\Android\Android Studio\bin\idea.properties中的

idea.config.path=D:/Android/.AndroidStudio/config
idea.system.path=D:/Android/.AndroidStudio/system

.gradle文件夹的修改

设置Android Studio中的
Settings->Build,Execution,Deployment->Gradle 中的Service directory path为D:/Android/.gradle

.android文件夹的修改
添加一个系统的环境变量ANDROID_SDK_HOME为D:/Android/avd

https://www.jianshu.com/p/7a58c5f154c5

Android 常用尺寸

密度ldpimdpihdpixhdpixxhdpixxxhdpi
比例0.7511.5234
密度数120dpi160dpi240dpi320dpi480dpi640dpi
代表分辨率240*320320*480480*800720*12801080*19201440*2560
Launcher And Home36*3648*4872*7296*96144*144192*192
Action Bar And Tab24*2432*3248*4864*6496*96128*128
Notification18*1824*2436*3648*4872*7296*96
Background320*426320*470480*640720*12801080*19201440*2560

Android Studio一直 Fetching Documentation…

修改“C:\Users\Administrator\.AndroidStudio2.3\config\options\jdk.table.xml”文件

<javadocPath>
  <root type="composite">
	<root type="simple" url="file://D:/Android/sdk/platforms/reference/" />
  </root>
</javadocPath>

Android 颜色透明度换算

100% — FF
95% — F2
90% — E6
85% — D9
80% — CC
75% — BF
70% — B3
65% — A6
60% — 99
55% — 8C
50% — 80
45% — 73
40% — 66
35% — 59
30% — 4D
25% — 40
20% — 33
15% — 26
10% — 1A
5% — 0D
0% — 00

TabLayout添加 tab分割线

    LinearLayout linearLayout = (LinearLayout) tabContent.getChildAt(0);
    linearLayout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
    linearLayout.setDividerPadding(CommonUtils.dp2px(this, 8));
    linearLayout.setDividerDrawable(ContextCompat.getDrawable(this,
            R.drawable.layout_divider_vertical));

layout_divider_vertical.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android">
	<solid android:color="@color/grayDark"/>
	<size android:width="1dp"/>
</shape>

将十六进制 颜色代码 转换为int类型数值

Color.parseColor(“#FF00FF”)

getColor在6.0中过时

MainActivity.this.getResources().getColor(R.color.colorTest)

用一下替换

    ContextCompat.getColor(context, R.color.my_color)
    //源码 public static final int getColor(Context context, int id) { 
    final int version = Build.VERSION.SDK_INT; 
        if (version >= 23) { 
            return ContextCompatApi23.getColor(context, id);
        } else {
            return context.getResources().getColor(id);  
        }
    }

SparseArray

SparseArray是android里为<Interger,Object>这样的Hashmap而专门写的class,目的是提高效率,其核心是折半查找函数(binarySearch)

Listview的Adapter的转换

如果ListView没有headerView或者footerView的时候,与listView相关联的Adapter就是传进来的参数Adapter,如果有,就将原来的Adapter包装成HeaderViewListAdapter,HeaderViewListAdapter有个方法getWrappedAdapter,该方法能返回被包装的HeaderViewListAdapter的ListAdapter。

HeaderViewListAdapter headerViewListAdapter = (HeaderViewListAdapter) listView.getAdapter();
ContactAdapter contactAdapter = (ContactAdapter) headerViewListAdapter.getWrappedAdapter();

使用View做虚线分割线

drawable/line_dash.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
           android:shape="line"
    
        >
        <stroke android:color="@color/colorBlack"
                android:dashGap="2dp"
                android:dashWidth="5dp"
                android:width="1dp"
                />
        <size android:width="1dp" />
    </shape>

布局文件中:(需要设置 layerType为software)

    <View
            android:layout_width="match_parent"
            android:layout_height="2dp"
            android:background="@drawable/line_dash"
            android:layerType="software"
            />

Android api23 删除HttpClient 相关类

在Module的gradle.build文件添加依赖,和 packagingOptions,和 useLibrary ‘org.apache.http.legacy’

    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.egrid.webdemo"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    packagingOptions {
            exclude 'META-INF/DEPENDENCIES'
            exclude 'META-INF/NOTICE'
            exclude 'META-INF/LICENSE'
            exclude 'META-INF/LICENSE.txt'
            exclude 'META-INF/NOTICE.txt'
        }
        //会加载Andrdoid自带的jar包 org.apache.http.legacy.jar
         useLibrary 'org.apache.http.legacy'
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.1.1'
        //下面这些不会起作用
        compile 'org.apache.httpcomponents:httpclient:4.3.6'
        compile 'org.apache.httpcomponents:httpcore:4.3.2'
    }

横竖屏切换

android:configChanges="orientation|keyboardHidden|screenSize"

移动Android Studio默认的AVD目录(.android)

Move .android folder to E:\Android\
Create environment variable called ANDROID_SDK_HOME and set its value to E:\Android

Setting environment variable in Windows XP:

	Right-click on My Computer and choose "Properties")
	Click the "Advanced" tab
	Click the button "Environment Variables".
	Add New variable

https://stackoverflow.com/questions/3109473/moving-default-avd-configuration-folder-android

获取当前Activity的根视图

View rootView = getWindow().getDecorView().findViewById(android.R.id.content); 
或者:
View rootView = findViewById(android.R.id.content); 
或者:
View rootView = findViewById(android.R.id.content).getRootView(); 
关于android.R.id.content,开发者文档中并没有给予说明,但经过测试它应该是用来获取setContentView()中设置的View。

http://www.cnblogs.com/littlepanpc/p/4506294.html

Activity启动模式

https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en

语言的复数形式

getQuantityString() 

<resources>
    <plurals
        name="plural_name">
        <item
            quantity=["zero" | "one" | "two" | "few" | "many" | "other"]
            >text_string</item>
    </plurals>
</resources>

https://developer.android.com/guide/topics/resources/string-resource.html#Plurals
http://blog.youkuaiyun.com/a78270528/article/details/46791533

应用运行检测

/**
 * 检测应用是否在后台运行
 * [@param](https://my.oschina.net/u/2303379) context context
 * [@return](https://my.oschina.net/u/556800) true 后台运行
 */
public static boolean isBackgroundRunning(final Context context) {
	ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
	List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(am.getRunningAppProcesses().size());
	for (ActivityManager.RunningTaskInfo runningTaskInfo : tasks) {
		if (runningTaskInfo.topActivity.getPackageName().equals(context.getPackageName())) {
			Log.d(TAG, "packageName:" + runningTaskInfo.topActivity.getPackageName());
			Log.d(TAG, "className:" + runningTaskInfo.topActivity.getClassName());
			return true;
		}
	}
	return false;
}

/**
 * 检测应用是否在后台运行
 * [@param](https://my.oschina.net/u/2303379) context context
 * [@return](https://my.oschina.net/u/556800) true 前台运行
 */
public static boolean isFrontRunning(Context context) {
	ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
	List<ActivityManager.RunningAppProcessInfo> runningProcesses = am.getRunningAppProcesses();
	for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
		if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
			for (String activeProcess : processInfo.pkgList) {
				if (activeProcess.equals(context.getPackageName())) {
					Log.d(TAG, "packageName:" + activeProcess);
					Log.d(TAG, "className:" + processInfo.processName);
					return true;
				}
			}
		}
	}
	return false;
}

app过大错误

问题:

Error:Execution failed for task ':app:transformClassesWithDexForDevDebug'.
> com.android.build.api.transform.TransformException: com.android.ide.common.process.ProcessException: java.util.concurrent.ExecutionException: com.android.dex.DexIndexOverflowException: method ID not in [0, 0xffff]: 65536

修改文件:

build.gradle 文件

android {
	compileSdkVersion 21
	buildToolsVersion "21.1.0"

	defaultConfig {
		...
		minSdkVersion 14
		targetSdkVersion 21
		...

		// Enabling multidex support.
		multiDexEnabled true
	}
	...
}

dependencies {
  compile 'com.android.support:multidex:1.0.0'
}

Application类

<application
    ...
    android:name="com.test.TestApplication">
    ...
</application>

public class TestApplication extends Application {

	protected void attachBaseContext(Context base) {
		super.attachBaseContext(base);
		MultiDex.install(this);
	}
}

详情:

https://developer.android.com/studio/build/multidex.html
http://stackoverflow.com/questions/26609734/how-to-enable-multidexing-with-the-new-android-multidex-support-library/38473900#38473900

Android 应用图标

m		1		48*48px
h		1.5		72*72px
xh		2		96*96px
xxh		3		144*144px
xxxh	4		192*192px

1.CTRL+A  再选择编辑中的“描边”就行。(选择自己喜欢的颜色、设置自己需要的宽度) 
2.新建图层--矩形选框--反选--填充--快速蒙板--滤镜(多用几个)--退出蒙板--图层效果
3.矩形选区--快速蒙板--滤镜(各种滤镜有不同的效果,可以同时用多个滤镜)--退出蒙板--反选--删除--描边

ios和安卓切图换算比例

1dp(Android)=1pt(iOS)

以48dp@160dpi计算的话

mdpi 48px (160dpi, 1x) 基础尺寸

hdpi 72px (240dpi, 1.5x)

xhdpi 96px (320dpi, 2x) 同iOS @2x

xxdpi 144px (480dpi, 3x) 同iOS @3x

xxxdpi 192px (640dpi, 4x)

链接:http://www.zhihu.com/question/27038815/answer/34992846

android网络请求与页面切换

在android开发中会遇到使用fragment切换页面,并且切换的页面都会有网络请求,网络请求成功后会更新相应的fragment页面。如果使用异步网络请求组件android-async-http,会出现这样的情况,当你快速的切换fragment时,每个fragment都会发出新的请求。例如:fragmentA发了网络请求,又切换到了fragmentB,fragmentB又发了网络请求,然后快速的切换两个fragment,切换多次后可能会出现,fragmentA发出的请求返回成功并且使用handler发出消息请求改变fragmentA中的UI时,这时当前的页面正是fragmentB。此时会报空指针错误。

解决此类问题,可以通过第一次加载时缓存,再次切换到此页面时,只有下拉才能请求新数据。另一种方法是,设置一个public static volatile int flag;变量,如果flag为1时则表示当前页面在fragmentA中,当flag为2时表示当前页面在fragmentB中,只当handler收到消息并且flag=1时才能更新fragmentA中的数据。第二种方法实现起来比较繁锁,推荐使用第一种方法。其中1、2、3这些最好存放在一个枚举类型中。

[转]http://blog.youkuaiyun.com/wode_dream/article/details/41480271

访问UI线程

1)Activity.runOnUiThread(Runnable)

2)View.post(Runnable)

public void onClick(View v) {
  new Thread(new Runnable() {
	public void run() {
	  final Bitmap b = loadImageFromNetwork();
	  mImageView.post(new Runnable() {
		public void run() {
		  mImageView.setImageBitmap(b);
		}
	  });
	}
  }).start();
}

3)View.postDelayed(Runnable, long)

4)Handler

final Handler myHandler = new Handler();

(new Thread(new Runnable() {

	@Override
	public void run() {
	   final Bitmap b = loadImageFromNetwork();
	  myHandler.post(new Runnable() {                           

		@Override
		public void run() {
		   mImageView.setImageBitmap(b);
		  }
		});
	  }
	})).start();                
}

ListView嵌套 父ListView高度设置

        /**
	 * 调整ListView嵌套后,根据子ListView的高度重设父ListView的高度
	 * @param listView
	 */
	public static void setListViewHeightBasedOnChildren(ListView listView) {
		ListAdapter listAdapter = listView.getAdapter();
		if (listAdapter == null) {
			return;
		}

		int totalHeight = 0;
		for (int i = 0; i < listAdapter.getCount(); i++) {
			View listItem = listAdapter.getView(i, null, listView);
			listItem.measure(0, 0);
			totalHeight += listItem.getMeasuredHeight();
		}

		ViewGroup.LayoutParams params = listView.getLayoutParams();
		params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
		listView.setLayoutParams(params);
	}

代码创建控件

    ImageView imageView = new ImageView(getActivity());
    //父控件是LinearLayout
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
           ViewGroup.LayoutParams.WRAP_CONTENT,
           ViewGroup.LayoutParams.WRAP_CONTENT);
    //相当于android:layout_gravity属性  
    params.gravity = Gravity.CENTER_VERTICAL;
    imageView.setLayoutParams(params);
    imageView.setScaleType(ImageView.ScaleType.FIT_XY);

    Button button = new Button(getActivity());
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
            ViewGroup.LayoutParams.WRAP_CONTENT,
            ViewGroup.LayoutParams.WRAP_CONTENT);
    button.setLayoutParams(params);
    //相当于android:gravity属性 
    button.setGravity(Gravity.CENTER_VERTICAL);

    linearLayout.addView(imageView);
    linearLayout.addView(button);

文本拼接

// %1表示第一个位置,$d表示数字,$s表示文本

    <string name="sale_comment_text1">%1$d】</string>
    <string name="sale_comment_text2">%1$s评价%2$s</string>

    getString(R.string.sale_comment_text1,pojo.getCommentType())
    getString(R.string.sale_comment_text2,pojo.getCustomerName(),pojo.getSalesName())
    
//    拼接文本为:【2】,张三评价李四

textAppearance

textAppearanceInverse/

textAppearanceLarge/

textAppearanceLargeInverse/

textAppearanceMedium/

textAppearanceSmallInverse/

textAppearanceMediumInverse/

textAppearanceSmall/

代码中加下划线
textView.getPaint().setFlags(Paint. UNDERLINE_TEXT_FLAG ); //下划线
textView.getPaint().setAntiAlias(true);//抗锯齿

转:http://www.cnblogs.com/-cyb/archive/2011/08/02/Android_textAppearance.html

gradle dependencies 设置 版本号

ext{
    compileLibVersion = '23.3.0'
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile "com.android.support:appcompat-v7:[$compileLibVersion]"
}

隐藏/移除ActionBar

    //隐藏掉整个ActionBar,包括下面的Tabs  
    getSupportActionBar().hide();
    //或
    requestWindowFeature(Window.FEATURE_NO_TITLE);  

    //会保留tab标签
    //高版本可以换成 ActionBar actionBar = getActionBar();  
    ActionBar actionBar = getSupportActionBar();
    actionBar.setDisplayShowTitleEnabled(false);  
    actionBar.setDisplayShowHomeEnabled(false);  

判断SDK版本

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            Window window = getWindow();
            // Translucent status bar
            window.setFlags(
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                    WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }

Android M运行权限问题

一行代码解决Android M新的运行时权限问题:
http://www.jianshu.com/p/d3a998ec04ad

Android Build.VERSION.SDK_INT兼容介绍

http://aijiawang-126-com.iteye.com/blog/1481572

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值