1.Touch处理
MotionEventCompat.getActionMasked(ev)
等价于
event.getAction() & ACTION_MASK
/**
* Call {@link MotionEvent#getAction}, returning only the {@link #ACTION_MASK}
* portion.
*/
public static int getActionMasked(MotionEvent event) {
return event.getAction() & ACTION_MASK;
}
2.枚举
enum
public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY
}
public enum Day {
MONDAY(1, "星期一", "星期一各种不在状态"),
TUESDAY(2, "星期二", "星期二依旧犯困"),
WEDNESDAY(3, "星期三", "星期三感觉半周终于过去了"),
THURSDAY(4, "星期四", "星期四期待这星期五"),
FRIDAY(5, "星期五", "星期五感觉还不错"),
SATURDAY(6, "星期六", "星期六感觉非常好"),
SUNDAY(7, "星期日", "星期日感觉周末还没过够。。。");
Day(int index, String name, String value) {
this.index = index;
this.name = name;
this.value = value;
}
private int index;
private String name;
private String value;
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
ENUM中的每一个值都是一个Object,它的每个声明都会占用运行时的部分内存以便能够引用到这个Object。因此ENUM的值会比对应的Integer和String所占用的内存多。过度在Android开发中使用ENUM将会增大DEX大小,并会增大运行时的内存分配大小。
Android中的注解方式
添加支持注解的依赖到你的项目中,需要在build.gradle文件中的依赖块中添加:
dependencies { compile 'com.android.support:support-annotations:24.2.0' }
IntDef和StringDef是两个神奇的注解常量,可以用来替代Enum的使用。这些注解能够帮助我们在编译时对变量赋值进行检查。
public static final int SUNDAY= 0;
public static final int MONDAY= 1;
public static final int TUESDAY= 2;
public static final int WEDNESDAY= 3;
...
@IntDef({SUNDAY, MONDAY, TUESDAY, WEDNESDAY,THURSDAY, FRIDAY, SATURDAY})
@Retention(RetentionPolicy.SOURCE)
public @interface Day{}
// 使用
@Day int today;
// Constants
public static final String WINTER = "Winter";
public static final String SPRING = "Spring";
public static final String SUMMER = "Summer";
public static final String FALL = "Fall";
// Declare the @ StringDef for these constants:
@ StringDef ({WINTER, SPRING, SUMMER, FALL})
@Retention(RetentionPolicy.SOURCE)
public @interface Season {}
// 在使用的时候,例如我们有一个变量名称为:
int color;
// 与此同时有一个函数:
void setColor(@Color int COLOR){
color = COLOR;
}
//在调用此函数的时候,参数名称如果不是IntDef中的变量名称的时候,例如
//setColor(2),Android Studio中就会提示错误(虽然编译仍然会通过)。
Android 性能:避免在Android上使用ENUM
Android开发中用于替代Enum的@IntDef的使用
3.getResources().getDrawable() 过时的解决方法
- 当你这个Drawable受当前Activity主题的影响时
ContextCompat.getDrawable(getActivity(), R.drawable.name);
- 当你这个Drawable不受主题影响时
ResourcesCompat.getDrawable(getResources(), R.drawable.name, null);
- 当你这个Drawable想使用另外一个主题样式时
ResourcesCompat.getDrawable(getResources(), R.drawable.name, anotherTheme);
4.填加分割线
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/setting_divider"
android:showDividers="middle"
android:background="@drawable/area_dialog_bg"
android:gravity="center">
5.获取屏幕宽高
1. 通过WindowManager获取
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
heigth = dm.heightPixels;
width = dm.widthPixels;
2. 通过Resources获取
DisplayMetrics dm = getResources().getDisplayMetrics();
heigth = dm.heightPixels;
width = dm.widthPixels;
3. 获取屏幕的默认分辨率
Display display = getWindowManager().getDefaultDisplay();
heigth = display.getWidth();
width = display.getHeight();
1、3都是使用getWindowManager()得到的,但这个是建立在类Activity上的,如果自己的类没有继承这个,则取不到数据,故个人认为通过Resources获取最好。
在非Activity类中,拿到WindowManager
((WindowManager) MyApplicationContext.context.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay()
.getMetrics(dm);
6.分享(不调用第三方SDK)
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, "来自WanAndroid" + detailLink);
startActivity(Intent.createChooser(shareIntent, "分享"));
7.RecyclerView点击条目自动滑动到中央
public class CenterLayoutManager extends LinearLayoutManager {
public CenterLayoutManager(Context context) {
super(context);
}
public CenterLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public CenterLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
private static class CenterSmoothScroller extends LinearSmoothScroller {
CenterSmoothScroller(Context context) {
super(context);
}
@Override
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
}
}
}
其中calculateDtToFit的参数,viewStart viewEnd表示被点击的条目需要移动的Start/End,viewEnd - viewStart = View的宽度;boxStart boxEnd表示当前RecyclerView的Start/End,boxEnd - boxStart = RecyclerView的宽度(无Padding)。
public int calculateDyToMakeVisible(View view, int snapPreference) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (layoutManager == null || !layoutManager.canScrollVertically()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
final int start = layoutManager.getPaddingTop();
final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
return calculateDtToFit(top, bottom, start, end, snapPreference);
}
8.自定义View的动画还可以这么搞
mTabSelector = new Runnable() {
public void run() {
final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
smoothScrollTo(scrollPos, 0);
mTabSelector = null;
}
};
post(mTabSelector);
9.RecyclerView滚动到当前界面第一个
scrollToPosition会把不在屏幕的 Item 移动到屏幕上,原来在上方的 Item 移动到 可见 Item 的第一项,在下方的移动到屏幕可见 Item 的最后一项。已经显示的 Item 不会移动。
scrollToPositionWithOffset会把 Item 移动到可见 Item 的第一项,即使它已经在可见 Item 之中。另外它还有 offset 参数,表示 Item 移动到第一项后跟 RecyclerView 上边界或下边界之间的距离(默认是 0)
10.MAC快捷键
大小写转换 Cmd + Shift + U Ctrl + Shift + U
注释代码(//) Cmd + / Ctrl + /
注释代码(/**/) Cmd + Option + / Ctrl + Alt + /
格式化代码 Cmd + Option + L Ctrl + Alt + L
清除无效包引用 Option + Control + O Alt + Ctrl + O
查找 Cmd + F Ctrl + F
查找+替换 Cmd + R Ctrl + R
上下移动代码 Option + Shift + Up/Down Alt + Shift + Up/Down
删除行 Cmd + Delete Ctrl + Y
扩大缩小选中范围 Option + Up/Down Ctrl + W/Ctrl + Shift + W
快捷生成结构体 Cmd + Option + T Ctrl + Alt + T
快捷覆写方法 Ctrl + O Ctrl + O
快捷定位到行首/尾 Cmd + Left/Right Ctrl + Left/Right
折叠展开代码块 Cmd + Plus,Minus Ctrl + Plus/Minus
折叠展开全部代码块 Cmd + Shift + Plus,Minus Ctrl + Shift + Plus,Minus
文件方法结构 Cmd + F12 Ctrl + F12
查找调用的位置 Ctrl + Option + H Ctrl + Alt + H
大小写转换 Cmd + Shift + U Ctrl + Shift + U
11.CoordinatorLayout相关注意事项
CoordinatorLayout继承自viewgroup,但是使用类似于framLayout,有层次结构,后面的布局会覆盖在前面的布局之上,但跟behavior属性也有很大关系,的app:layout_behavior属性,只有CoordinatorLayout的直接子布局才能响应,所以不要做徒劳无功的事
CoordinatorLayout+AppBarLayout+CollapsingToolbarLayout结合起来才能产生这么神奇的效果,不要想当然的光拿着CoordinatorLayout就要玩出来(刚接触的时候我也有这种想法),累死你
AppBarLayout:继承自lineLayout,使用时其他属性随lineLayou,已经响应了CoordinatorLayout的layout_behavior属性,所以他能搞出那么多特效来
AppBarLayout的直接子控件可以设置的属性:layout_scrollFlags
1.scroll|exitUntilCollapsed如果AppBarLayout的直接子控件设置该属性,该子控件可以滚动,向上滚动NestedScrollView出父布局(一般为CoordinatorLayout)时,会折叠到顶端,向下滚动时NestedScrollView必须滚动到最上面的时候才能拉出该布局
2.scroll|enterAlways:只要向下滚动该布局就会显示出来,只要向上滑动该布局就会向上收缩
3.scroll|enterAlwaysCollapsed:向下滚动NestedScrollView到最底端时该布局才会显示出来
4.如果不设置改属性,则改布局不能滑动
- CollapsingToolbarLayout,字面意思是折叠的toolbar,它确实是起到折叠作用的,可以把自己的自布局折叠 继承自framLayout,所以它的直接子类可以设置layout_gravity来控制显示的位置,它的直接子布局可以使用的属性:app:layout_collapseMode(折叠模式):可取的值如下:
1.pin:在滑动过程中,此自布局会固定在它所在的位置不动,直到CollapsingToolbarLayout全部折叠或者全部展开
2.parallax:视察效果,在滑动过程中,不管上滑还是下滑都会有视察效果,不知道什么事视察效果自己看gif图(layout_collapseParallaxMultiplier视差因子 0~1之间取值,当设置了parallax时可以配合这个属性使用,调节自己想要的视差效果)
3.不设置:跟随NestedScrollView的滑动一起滑动,NestedScrollView滑动多少距离他就会跟着走多少距离

toobar最好是放在CollapsingToolbarLayout,也不是没有其他用法,但是在这套系统中一般只能放在CollapsingToolbarLayout里面,才能达到好的效果,这里toolbar同时设置layout_gravity和app:layout_collapseMode时有一些比较复杂的情况.不一一作介绍,因为一般我们只会把toolbar放在最上面(不用设置layout_gravity属性,默认的),并且设置app:layout_collapseMode为pin,让他固定在最顶端,有兴趣的自己试试其他情况,
告你一个惊天大幂幂:只要CollapsingToolbarLayout里面包含有toolbar那么CollapsingToolbarLayout的折叠后高度就是toolbar的高度,相当于CollapsingToolbarLayout设置了minHeight属性,
再告诉你一个惊天大咪咪:CollapsingToolbarLayout折叠到最顶端时,它就是老大,会处于最上层,包括toolbar在内,所有的布局都会被他盖住,显示不出来.
CollapsingToolbarLayout 自己的属性 说明,不是直接子布局可用的,是自己可以用的属性
contentScrim折叠后的颜色也是展开时的渐变颜色,效果超赞.
title标题,如果设置在折叠时会动画的显示到折叠后的部分上面,拉开时会展开,很好玩的
expandedTitleMargin当title文字展开时文字的margin,当然还有marginTop等属性,脑补吧
app:collapsedTitleTextAppearance=”@style/Text”折叠时title的样式里面可以定义字体大小颜色等
app:collapsedTitleTextAppearance=”@style/Text1”折叠时title的样式里面可以定义字体大小颜色等
当然还有一些,自己试试吧,现在的这些已经完全够用了还有最后一个问题:怎么实现固定表头,这个也简单,写一个布局放在CollapsingToolbarLayout之后,AppBarLayout之内即可,xml文件中自己找找看吧.你要是问如果放在CollapsingToolbarLayout之前,AppBarLayout之内会怎么样?这样折叠布局就不起作用了.不会折叠了.
https://blog.csdn.net/qq_31340657/article/details/51918773
12. startActivities
fun multiIntent(view : View) {
val intent_1 = Intent(this,IntentActivityA::class.java)
val intent_2 = Intent(this,IntentActivityB::class.java)
val intent_3 = Intent(this,IntentActivityC::class.java)
startActivities(arrayOf(intent_1,intent_2,intent_3))
}
IntentActivity C onCreate
IntentActivity B onCreate
IntentActivity C onDestroy
IntentActivity A onCreate
IntentActivity B onDestroy
IntentActivity A onDestroy
注意不是全部创建压入栈里的。相邻原则!!!
13.代码中更改margin
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) tv.getLayoutParams();
layoutParams.setMargins(0, 0, DisplayUtils.dip2px(Main2Activity.this,12), DisplayUtils.dip2px(Main2Activity.this,12));
tv.setLayoutParams(layoutParams);
注意:getLayoutParams()获取到的是tv的父布局的类型!!!!!如果它的父布局是FrameLayout而你转成LinearLayout.LayoutParams。程序会报错!
14.Gson中的TypeToken使用
List<AdBean> result = new Gson().fromJson(cacheContent, new TypeToken<List<AdBean>>() { }.getType());
使用它可以将String转换成List数组结构。
15. SparseArray和SparseArrayCompat
SparseArray以键值对的形式保存数据,key是int类型,并且是唯一的不允许重复的key,而value可以是任何object。
优点
相比HashMap更加节省内存空间,数据存储只依赖key和value2个数组,数组空间是可复用的,数据的存储密度较高。
因为key数组是有序的,通过key获取value相对高效。
缺点:
key数组是保持有序的,在插入和查找时,通过二分法确定位置key的下标,比较耗时;
插入删除等操作可能会移动数组数据。
综合来说,SparseArray适用于小数据量的键值对场景。数据量达到几百时,效率优势和HashMap相比已不明显。
SparseArray只能在API19以上的系统里面 才有这个类,也就是Android4.4以上。所以Android为我们提供了一个兼容的类SparseArrayCompat,使用这个可以兼容更低的版本。
private static final int BASE_ITEM_TYPE_HEADER = 100000;
private SparseArrayCompat<View> mHeaderViews = new SparseArrayCompat<>();
public void addHeaderView(View view) {
mHeaderViews.put(mHeaderViews.size() + BASE_ITEM_TYPE_HEADER, view);
}
@Override
public int getItemViewType(int position) {
if (isHeaderViewPos(position)) {
return mHeaderViews.keyAt(position);
} else if (isFooterViewPos(position)) {
return mFootViews.keyAt(position - getHeadersCount() - getRealItemCount());
}
return mInnerAdapter.getItemViewType(position - getHeadersCount());
}
16.隐藏状态栏
<style name="AppLaunch" parent="@android:style/Theme.Light.NoTitleBar.Fullscreen">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@drawable/app_window_bg</item>
<item name="windowActionBar">false</item>
<item name="android:windowFullscreen">true</item>
<item name="android:windowContentOverlay">@null</item>
</style>
设置全屏会自动隐藏状态栏!!!但是不会隐藏标题栏,所以想要全屏需要windowNoTitle、windowFullscreen一起设置!
17.List<T>中的T可以为接口
效果同List<?extends MyInterface>
18.从View中获取Context的正确姿势
在继承自AppCompatActivity时,Android底层会将我们应用的控件转为v7包中对应的控件,Context就被替换成了TintContextWrapper(它继承自ContextWrapper)。
或者
View 可能继承自 AppCompat 系的 View (比如 AppCompatTextView, AppCompatImageView),此时的context存储于TintContextWrapper的getBaseContext中,所以需要以下处理:
Context context = v.getContext();
if (context instanceof TintContextWrapper) {
context = ((TintContextWrapper) context).getBaseContext();
}
if (context instanceof Activity) {
((Activity) context).finish();
}
或者
public static Activity getActivityFromView(View view) {
Context context = view.getContext();
while (context instanceof ContextWrapper) {
if (context instanceof Activity) {
return (Activity) context;
}
context = ((ContextWrapper) context).getBaseContext();
}
return null;
}
Android 从 View 中获取 Activity 时遇到 TintContextWrapper cannot be cast to 的问题
19.文件读写操作时,先进行判断
public static String getCachePath(Context context) {
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
// 有sd卡
try {
return context.getExternalCacheDir().getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
return context.getCacheDir().getAbsolutePath();
}
} else {
// 无sd卡
return context.getCacheDir().getAbsolutePath();
}
}
20.点击通知的跳转逻辑
startActivities(new Intent[]{mainIntent, targetIntent});
这样的话,先跳转目标页,点击返回跳转到主页面。
21.RecyclerView的小技巧
使内容等间距排列,spanCount是个数,spacing是两个Item的间距。
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view); // item position
int column = position % spanCount; // item column
outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
if (position >= spanCount) {
outRect.top = spacing; // item top
}
}
22.判断intent是否能跳转/是否有效
public static boolean checkIntent(Intent intent) {
return checkIntent(intent, PackageManager.GET_INTENT_FILTERS);
}
public static boolean checkIntent(Intent intent, int flags) {
PackageManager pm = RootInit.getInstance().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(intent, flags);
final int size = (activities == null) ? 0 : activities.size();
return size > 0;
}
23.WebView配置
private void initWebViewSetting() {
WebSettings ws = getSettings();
try {
ws.setJavaScriptEnabled(true);
// Logger
} catch (Exception e) {
e.printStackTrace();
}
ws.setSupportZoom(false);
String appCacheDir = Paths.getInternalCachePath(getContext());
ws.setDomStorageEnabled(true);
ws.setAppCacheMaxSize(1024 * 1024 * 4);//设置缓冲
ws.setAppCachePath(appCacheDir);
ws.setAllowFileAccess(true);
ws.setAppCacheEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ws.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
ws.setJavaScriptCanOpenWindowsAutomatically(true);
ws.setDefaultTextEncodingName("utf-8");
ws.setUseWideViewPort(true);
ws.setLoadWithOverviewMode(true);
ws.setBuiltInZoomControls(false);
// webView.setBackgroundColor(0);
ws.setCacheMode(WebSettings.LOAD_NO_CACHE);
removeJavascriptInterface("searchBoxJavaBridge_");
removeJavascriptInterface("accessibility");
removeJavascriptInterface("accessibilityTraversal");
ws.setAllowFileAccess(true);// 设置允许访问文件数据
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ws.setAllowUniversalAccessFromFileURLs(false);
ws.setAllowFileAccessFromFileURLs(false);
}
setWebChromeClient(new BpWebChromeClient());
setWebViewClient(new BpWebViewClient());
setDownloadListener(new DownloadListener() {
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
if (null != url) {
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
if (ToolBox.checkIntent(intent)) {
try {
getActivity().startActivity(intent);
} catch (Throwable e) {
YLog.e(e);
}
}
}
}
});
}
24. PopupWindow 沉浸式
pop.setClippingEnabled(false)
25. Dialog 沉浸式
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
}
26.android studio预览可视化
if (thisView.isInEditMode()) {//这段代码在运行时不会执行,只会在Studio编辑预览时运行,不用在意性能问题
final int d = SmartUtil.dp2px(5);
final Context context = thisView.getContext();
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(0xcccccccc);
paint.setStrokeWidth(SmartUtil.dp2px(1));
paint.setPathEffect(new DashPathEffect(new float[]{d, d, d, d}, 1));
canvas.drawRect(d, d, thisView.getWidth() - d, thisView.getBottom() - d, paint);
TextView textView = new TextView(context);
textView.setText(context.getString(R.string.srl_component_falsify, getClass().getSimpleName(), SmartUtil.px2dp(thisView.getHeight())));
textView.setTextColor(0xcccccccc);
textView.setGravity(Gravity.CENTER);
//noinspection UnnecessaryLocalVariable
View view = textView;
view.measure(makeMeasureSpec(thisView.getWidth(), EXACTLY), makeMeasureSpec(thisView.getHeight(), EXACTLY));
view.layout(0, 0, thisView.getWidth(), thisView.getHeight());
view.draw(canvas);
}
27.descendantFocusability子布局获取焦点
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
注意:它只在RelativeLayout中设置生效!!!
28.注解生命周期
-
RetentionPolicy.SOURCE源码注解,编译成.class文件后注解就不存在,用来提示开发者 -
RetentionPolicy.CLASS CLASS汇编注解,编译成.class文件后注解也还存在,用于自动生成代码 -
RetentionPolicy.RUNTIME运行时动态注解,生命周期一直程序运行时都存在,常用于自动注入
29.绘制完加载布局
final View layout = findViewById(Window.ID_ANDROID_CONTENT);
ViewTreeObserver vto = layout.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
layout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
} else {
layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
}
initUIandEvent();
}
});
30.光晕效果(蒙层)
BlurMaskFilter
/**
* This takes a mask, and blurs its edge by the specified radius. Whether or
* or not to include the original mask, and whether the blur goes outside,
* inside, or straddles, the original mask's border, is controlled by the
* Blur enum.
*/
/* 翻译成大白话的意思就是BlurMaskFilter可以在原本的View上添加一层指定模糊半径的蒙层,具体模糊的方式,由Blur枚举类型控制 */
// 光晕的paint
private val outPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
// 光晕的颜色
color = Color.parseColor("#e6e8db")
// 使用BlurMaskFilter制作阴影效果
maskFilter = BlurMaskFilter(shadowRadius.toFloat(), BlurMaskFilter.Blur.SOLID)
}
31.RecyclerView融边效果
android:fadingEdgeLength="30dp"
android:requiresFadingEdge="horizontal"
32.DialogFragment全屏显示
final Window window = getDialog().getWindow();
window.setBackgroundDrawableResource(R.color.transparent);
window.getDecorView().setPadding(0, 0, 0, 0);
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
注意:第二行和第三行必须设置才能全屏,亲测!
33. 自定义控件调整位置
offsetTopAndBottom、offsetLeftAndRight
43.键盘弹出背景不动
手机屏幕的高为1920px,那么整个Activity的布局高度也为1920px。当设置该属性后点击界面中的EditText,此时弹出软键盘其高度为800px。为了完整地显示此软键盘,系统会调整Activity布局的高度为1920px-800px=1120px。
当 windowSoftInputMode 被设置为 adjustResize 时候,当布局调整的时候被调整的布局均会重绘制,并走了onMeasure,onSizeChanged,onLayout 。
当 windowSoftInputMode 被设置为 adjustPan 时候,当布局调整的时候被调整的布局均会重绘制,并走了onMeasure, onLayout 。
这里只需要注意 两者都走了 onMeasure 方法,至于 adjustPan 没走 onSizeChanged。
所以只需重写onMeasure方法,即可实现。
44.PopupWindow透传致其它View响应点击事件
解决:只需要让PopupWindow持有焦点即可。
this.setFocusable(true);
或
//最后一个参数为true
new PopupWindow(view, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
45. dialog相关背景设置
1.设置透明度(Dialog自身的透明度)
WindowManager.LayoutParams lp=dialog.getWindow().getAttributes();
lp.alpha=1.0f;
dialog.getWindow().setAttributes(lp);
alpha在0.0f到1.0f之间。1.0完全不透明,0.0f完全透明
2.设置黑暗度(Dialog自身的黑暗度)
dialog.setContentView(R.layout.dialog);
WindowManager.LayoutParams lp=dialog.getWindow().getAttributes();
lp.dimAmount=1.0f;
dialog.getWindow().setAttributes(lp);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
dimAmount在0.0f和1.0f之间,0.0f完全不暗,1.0f全暗
3.设置Dialog底背景模糊和黑暗度
WindowManager.LayoutParams.FLAG_BLUR_BEHIND(设置模糊)
WindowManager.LayoutParams.FLAG_DIM_BEHIND(设置暗淡)
4.清除Dialog底背景模糊和黑暗度
getDialog().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND | WindowManager.LayoutParams.FLAG_DIM_BEHIND)
46.防止多点触控
android:splitMotionEvents=false
全局禁用:
<style name="MyStyle">
<item name="android:windowEnableSplitTouch">false</item>
<item name="android:splitMotionEvents>false</item>
</style>
47.DialogFragment 在5.0手机上适配的各种问题
如果出现弹窗在5.0手机上位置不对,设置Gravity不好使,大小尺寸不对,你可能需要如下设置来解决
1、布局
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/add_friend_popup_bg">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0.5dp"
android:layout_marginBottom="0.5dp"
android:fadeScrollbars="true" />
</FrameLayout>
</FrameLayout>
你需要在内容外部在再套一层,这样弹窗大小的问题就能解决!
2、设置主题
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 必须设置,不然会出现5.0及以下手机不适配
setStyle(DialogFragment.STYLE_NORMAL, R.style.DialogStyle);
}
<style name="DialogStyle" parent="@android:style/Theme.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowFrame">@null</item>
<item name="android:backgroundDimEnabled">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
</style>
5.0及以下手机dialogFragment 有默认主题,显示位置不对一般是这个导致的!
48.虚拟导航键显示判断
public static boolean isNavBarVisible(Context context) {
ViewGroup rootLinearLayout = findRootLinearLayout(context);
int navigationBarHeight = 0;
if (rootLinearLayout != null) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) rootLinearLayout.getLayoutParams();
navigationBarHeight = layoutParams.bottomMargin;
}
return navigationBarHeight != 0;
}
private static ViewGroup findRootLinearLayout(Context context) {
Activity activity = ActivityUtils.getActivityByContext(context);
if (activity != null) {
Window window = activity.getWindow();
if (window != null) {
ViewGroup decorView = (ViewGroup) window.getDecorView();
if (decorView != null) {
View contentView = activity.findViewById(android.R.id.content);
if (contentView != null) {
ViewGroup parent = (ViewGroup) contentView.getParent();
while (parent != decorView) {
if (parent == null) {
return null;
}
if (parent instanceof LinearLayout && !(parent instanceof FitWindowsLinearLayout)) {
return parent;
}
parent = (ViewGroup) parent.getParent();
}
}
}
}
}
return null;
}
51.recyclerView禁用多指
android:splitmotionevents=”false”
或者
recyclerView.setMotionEventSplittingEnabled(false);
52.recyclerView滑动到指定位置,并指定位置在顶部
1.第一种方法
此方法能实现指定位置位于屏幕顶部,但是不具有平滑滚动视觉效果:
if (position != -1) {
mRecycleview.scrollToPosition(position);
LinearLayoutManager mLayoutManager =
(LinearLayoutManager) mRecycleview.getLayoutManager();
mLayoutManager.scrollToPositionWithOffset(position, 0);
}
2.第二种方法
此方法能实现指定位置位于屏幕顶部,具有平滑滚动视觉效果:
首先获取第一个可见位置和最后一个可见位置,分三种情况:
1.如果如果跳转位置在第一个可见位置之前,就smoothScrollToPosition()可以直接跳转;
2.如果跳转位置在第一个可见项之后,最后一个可见项之前smoothScrollToPosition()不会滚动,此时调用smoothScrollBy来滑动到指定位置;
3.如果要跳转的位置在最后可见项之后,则先调用smoothScrollToPosition()将要跳转的位置滚动到可见位置,在addOnScrollListener()里通过onScrollStateChanged控制,调用smoothMoveToPosition,再次执行判断;
//目标项是否在最后一个可见项之后
private boolean mShouldScroll;
//记录目标项位置
private int mToPosition;
/**
* 滑动到指定位置
*/
private void smoothMoveToPosition(RecyclerView mRecyclerView, final int position) {
// 第一个可见位置
int firstItem = mRecyclerView.getChildLayoutPosition(mRecyclerView.getChildAt(0));
// 最后一个可见位置
int lastItem = mRecyclerView.getChildLayoutPosition(mRecyclerView.getChildAt(mRecyclerView.getChildCount() - 1));
if (position < firstItem) {
// 第一种可能:跳转位置在第一个可见位置之前
mRecyclerView.smoothScrollToPosition(position);
} else if (position <= lastItem) {
// 第二种可能:跳转位置在第一个可见位置之后
int movePosition = position - firstItem;
if (movePosition >= 0 && movePosition < mRecyclerView.getChildCount()) {
int top = mRecyclerView.getChildAt(movePosition).getTop();
mRecyclerView.smoothScrollBy(0, top);
}
} else {
// 第三种可能:跳转位置在最后可见项之后
mRecyclerView.smoothScrollToPosition(position);
mToPosition = position;
mShouldScroll = true;
}
}
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (mShouldScroll&& RecyclerView.SCROLL_STATE_IDLE == newState) {
mShouldScroll = false;
smoothMoveToPosition(irc, mToPosition);
}
}
});
if (position != -1) {
smoothMoveToPosition(irc,position);
}else {
smoothMoveToPosition(irc,position+1);
}
53.RecyclerVIew内的Item布局超出来了?
事情是这样的,项目升级androidx后发现,RecyclerVIew内的Item布局超出来,即外层限制不住它了,相关代码:
GridLayoutManager.LayoutParams params = (GridLayoutManager.LayoutParams) rvVoiceRoom.getLayoutParams();
//超过一行需要动态修改左边和底部的距离
params.setMargins(DisplayUtil.dip2px(getContext(), 14), 0, 0, 0);
rvVoiceRoom.setLayoutParams(params);
GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), spanCount, GridLayoutManager.HORIZONTAL, false);
rvVoiceRoom.setLayoutManager(gridLayoutManager);
明明限制了左边距是14dp,为什么显示效果是直接加在第一个item里了呢,无法限制整个滑动的区域。
查看布局文件发现,这个RecyclerView没有父布局。
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rv_voice_room"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never"/>
由此可以想到,应该是androidx修改了RecyclerView的源码,当RecyclerView没有父布局时,对他的layoutParam(GridLayoutManager.LayoutParams)进行修改,相当于在加在第一个item(最后一个 item里)。
故,修复这个问题也很简单,套一层父布局即可解决,布局修改如下:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_voice_room"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:overScrollMode="never" />
</FrameLayout>
别忘了修改params的类型
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) rvVoiceRoom.getLayoutParams();
效果和分析的一样,确实是这个原因。有时间还是比较下源码,Mark!!!todo