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 更加友好
- 提示音
//播放系统提示音提示下载完成
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())
- 携程的使用。
回到的函数,让其就行回调使用
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
- 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';
}
}
}
网友评论