highlight: androidstudio
工具&网站
Activity中动态设置 SoftInputMode
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
Activity 中动态设置 SoftInputMode 必要条件: AndroidManifest.xml <activity>中需要配置 android:WindowSoftInputMode="xxxx",
若没有配置, 则代码中如上, 达不到预期的效果
Git Push 到远程仓库后,回退到某个版本
- git log 查看要回退的版本号
- git reset --soft <版本号>
- git push origin 分支名(develop/master) --force 将回退结果推到远程仓库
AndroidStudio
1.AndroidStudio 自己安装的插件所在位置: www.jianshu.com/p/96daa6f99…
/Users/[username]/Library/Application Support/Google/AndroidStudio2020.3/plugins
- AndroidSutdio 配置目录:
/Users/xing/Library/Application Support/Google
才知道的技术点
- ImageView src 属性可以设置颜色值 android:src=“#ff0000”
- 可以将 TabLayout 放在 ViewPager 内部
<androidx.viewpager.widget.ViewPager
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top" />
</androidx.viewpager.widget.ViewPager>
List 去重最好方案
/**
* 主要利用 LinkedHashSet 去重
* 1. 删除重复元素
* 2. 使用 LinkedHashSet保证删除元素后,新 List 中元素仍然保持原来的顺序
*
* @param list
* @return
*/
private List<String> removeSameInList(List<String> list) {
System.out.println("origin list: " + list);
LinkedHashSet<String> hashSet = new LinkedHashSet<>(list);
List<String> resultList = new ArrayList<>(hashSet);
System.out.println("Duplicate removed List: " + resultList);
return resultList;
}
// removeSameInList(Arrays.asList("22", "23", "33", "11", "22", "11", "55"))
// origin list: [22, 23, 33, 11, 22, 11, 55]
// Duplicate removed List: [22, 23, 33, 11, 55]
删除 URL 中的指定参数
public static String removeQueryName(String linkurl, String... names) {
if (names == null) {
return linkurl;
}
Uri uri = Uri.parse(linkurl);
Set<String> parameterNames = uri.getQueryParameterNames();
Set<String> tempSet = new HashSet<>(parameterNames);
for (String name : names) {
tempSet.remove(name);
}
StringBuilder param = new StringBuilder();
if (linkurl.contains("?")) {
param.append(linkurl.substring(0, linkurl.indexOf("?")));
} else {
param.append(linkurl);
}
if (tempSet.size() > 0) {
param.append("?");
for (String parameterName : tempSet) {
param.append(parameterName)
.append("=")
.append(uri.getQueryParameter(parameterName))
.append("&");
}
return param.substring(0, param.toString().length() - 1);
}
return param.toString();
}
public static HashMap<String, String> getParams(@NonNull Uri uri, @NonNull Set<String> keys) {
HashMap<String, String> params = new HashMap<>();
for (String key: keys) {
if (uri.getQueryParameter(key) == null) {
continue;
}
params.put(key, uri.getQueryParameter(key));
}
return params;
}
public static String addParams(String url, Map<String, String> params) {
if (TextUtils.isEmpty(url)) {
return "";
}
if (params == null || params.isEmpty()) {
return url;
}
Uri uri = Uri.parse(url);
Map<String, String> uriParams = getParams(uri, uri.getQueryParameterNames());
uriParams.putAll(params);
Uri.Builder uriBuilder = uri.buildUpon();
uriBuilder.clearQuery();
for (Map.Entry<String, String> entry : uriParams.entrySet()) {
uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
}
return uriBuilder.build().toString();
}
/**
* 删除指定的url参数
*
* @param url - url字符串
* @param names - 要删除的参数名
* @return 删除指定参数后的url
*/
public static String removeParams(String url, String... names) {
if (TextUtils.isEmpty(url)) {
return "";
}
if (names == null || names.length == 0) {
return url;
}
Uri uri = Uri.parse(url);
if (uri == null) {
return url;
}
Set<String> parameterNames;
parameterNames = uri.getQueryParameterNames();
Map<String, String> params = getParams(uri, parameterNames);
for (String name: names) {
params.remove(name);
}
Uri.Builder uriBuilder = uri.buildUpon();
uriBuilder.clearQuery();
for (Map.Entry<String, String> entry : params.entrySet()) {
uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
}
return uriBuilder.build().toString();
}
自定义View过度绘制
Canvas.clipRect()
public class MultiBitmapView extends View {
private Paint paint;
private Bitmap bitmap;
private int count = 2;
private int pieWidth;
public MultiBitmapView(Context context) {
this(context, null);
}
public MultiBitmapView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MultiBitmapView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
pieWidth = w / count;
}
private void init(Context context) {
setWillNotDraw(false);
paint = new Paint();
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < count; i++) {
canvas.save();
canvas.clipRect(i * pieWidth, 0, (i + 1) * pieWidth, getHeight()); // 显示区域
canvas.drawBitmap(bitmap, 0, 0, paint);
/*if(i==1) {
isReject = canvas.quickReject(10 + i * 180, 10 + i * 180, 10 + i * 180+50, 10 + i * 180+50, Canvas.EdgeType.BW);
Log.i(TAG,"isReject : "+isReject);
isReject = canvas.quickReject(10 , 10 , 10 +180, 10 +180, Canvas.EdgeType.BW);
Log.i(TAG,"*** isReject : "+isReject);
}*/
canvas.restore();
}
}
}
RxJava Subject
/**
* BehaviorSubject: 只会接收订阅之前的最后一条数据和订阅之后的所有数据
* AsyncSubject: 无论何时订阅,发送多少数据,都只会接收最后一条数据
* ReplaySubject: 接收所有数据
* PublishSubject: 只会接收订阅之后的所有数据
*/
ArrayList, Array 打印
List<User> userList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
userList.add(new User("ctom", 12 + i));
}
System.out.println(userList);
User[] ss = new User[]{new User("ccc", 11), new User("ccc", 11)};
System.out.println(Arrays.toString(ss));
判断数组中是否包含元素
String[] cc = new String[10000];
for (int i = 0; i < cc.length; i++) {
cc[i] = "test-" + i;
}
Arrays.asList(cc).contains("test-9999");
Fragment 通信
Fragment 调用 Activity 方法
/// HomeFragment.java
public class HomeFragment extends Fragment {
private OnFragmentDataListener onFragmentDataListener;
public interface OnFragmentDataListener {
void onFragmentData(String text);
}
public HomeFragment() {
// Required empty public constructor
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof OnFragmentDataListener) {
onFragmentDataListener = (OnFragmentDataListener) context;
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button button = view.findViewById(R.id.btn_send);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (onFragmentDataListener != null) {
onFragmentDataListener.onFragmentData("from fragment data");
}
}
});
}
}
/// MianActivity.java
public class MainActivity extends AppCompatActivity implements
HomeFragment.OnFragmentDataListener {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.tv_text);
}
@Override
public void onFragmentData(String text) {
textView.setText(text);
}
}
Activity 调用 Fragment 方法
通过 FragmentManager 找到 Fragment, 调用其公开方法, 或者 直接通过将 Fragment 实例对象作为成员变量,调用其公开方法。
Fragment fragment = getSupportFragmentManager().findFragmentByTag("tag");
if(fragment instanceof HomeFragment) {
((HomeFragment) fragment).updateText();
}
子 Fragment 调用 父 Fragment 方法
/// ParentChildListener.java
public interface ParentChildListener {
void sendToParent(String text);
}
/// ChildFragment.java
public class ChildFragment extends Fragment {
private ParentChildListener listener;
public void setParentChildListener(ParentChildListener listener) {
this.listener = listener;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button childBtn = view.findViewById(R.id.btn_child);
childBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (listener != null) {
listener.sendToParent("send to parent");
}
}
});
}
}
/// ParentFragment.java
public class ParentFragment extends Fragment {
private TextView textView;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Button button = view.findViewById(R.id.btn_parent);
textView = view.findViewById(R.id.tv_result);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
addChildFragment();
}
});
}
private void addChildFragment() {
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
ChildFragment childFragment = ChildFragment.newInstance();
childFragment.setParentChildListener(new ParentChildListener() {
@Override
public void sendToParent(String text) {
textView.setText(text);
}
});
ft.add(R.id.fl_parent, childFragment);
ft.commitAllowingStateLoss();
}
}
自定义组合Layout
// 组合控件形式自定义标题栏 Layout
public class TitleBar extends RelativeLayout {
public TitleBar(Context context) {
super(context);
init();
}
public TitleBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TitleBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.layout_title_bar, this, true);
}
}
// layout_title_bar.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_baseline_arrow_back_24" />
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:src="@drawable/ic_baseline_menu_24" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="标题" />
</merge>
注意: 这里 layout_title_bar.xml 根布局使用 <merge> 而不是 <RelativeLayout>, 因为 <merge> 能够减少布局层级
使用 require 或者 check 函数作为条件检查
// 传统的做法
val age = -1;
if (age <= 0) {
throw IllegalArgumentException("age must not be negative")
}
// 使用 require 去检查
require(age > 0) { "age must be negative" }
// 使用 checkNotNull 检查
val name: String? = null
checkNotNull(name){
"name must not be null"
}
防抖&节流
fun View.setOnClickListener(debounceTime: Long = 700, block: (View) -> Unit) {
setOnClickListener(object : View.OnClickListener {
private var lastClickTime: Long = 0
override fun onClick(view: View) {
if (SystemClock.elapsedRealtime() - lastClickTime > debounceTime) {
block.invoke(view)
}
lastClickTime = SystemClock.elapsedRealtime()
}
})
}
fun View.throttleClick(delayTime: Long = 600, block: () -> Unit) {
setOnClickListener(object : View.OnClickListener {
private var timer: Timer? = null
override fun onClick(view: View?) {
timer?.cancel()
timer = Timer().apply {
schedule(timerTask {
block.invoke()
}, delayTime)
}
}
})
}
ViewPager 正确设置 FragmentAdapter, 获取指定位置上的 Fragment 实例
public class ViewPagerAdapter extends FragmentPagerAdapter {
private final FragmentManager fragmentManager;
private final List<PageData> dataList;
public ViewPagerAdapter(@NonNull FragmentManager fm, List<PageData> list) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.fragmentManager = fm;
this.dataList = list;
}
/**
* 注意 >>>>>>>>>>>>: 此方法由系统调用, 开发人员不应主动调用,
* <p>
* 若想获取指定位置的 Fragment 实例, 可调用方法: {@link ViewPagerAdapter#getFragmentByPosition(ViewGroup, int)}
*/
@NonNull
@Override
public Fragment getItem(int position) {
return createFragment(position);
}
private Fragment createFragment(int position) {
switch (position) {
case 1:
return SecondFragment.newInstance();
case 2:
return ThirdFragment.newInstance();
default:
return FirstFragment.newInstance();
}
}
@Override
public int getCount() {
return dataList == null ? 0 : dataList.size();
}
/**
* 清空 ViewPager中的 Fragment, 重新填充新的 Fragment
* <p>
* <p>
* 使用:
* ViewPagerAdapter adapter;
* if(adapter != null) {
* adapter.clearFragments(viewPager);
* }
* adapter = new ViewPagerAdapter(getSupportManager(), list);
* viewPager.setAdapter(adapter);
*
* @param container viewPager
*/
public void clearFragments(ViewGroup container) {
FragmentTransaction ft = fragmentManager.beginTransaction();
for (int i = 0; i < dataList.size(); i++) {
long itemId = getItemId(i);
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = fragmentManager.findFragmentByTag(name);
if (fragment != null) { // 根据对应的ID,找到fragment,删除
ft.remove(fragment);
}
}
ft.commitNowAllowingStateLoss();
}
private String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
/**
* 外部通过 ViewPager 获取指定位置上的 Fragment
* @param container
* @param position
* @return
*/
public Fragment getFragmentByPosition(ViewGroup container, int position) {
String name = makeFragmentName(container.getId(), getItemId(position));
return fragmentManager.findFragmentByTag(name);
}
}
TextView 排版
右侧TextView恒定存在, 左侧宽度自适应
1. LinearLayout 实现方式:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- 外层 LinearLayout 宽度必须为 match_parent 或固定为 xxdp -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#00897B"
android:ellipsize="end"
android:maxLines="1"
android:text="我从坚实的查克拉时间长了参加擦手机电池实力差距阿实力差距"
android:textSize="18sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F4511E"
android:text="我是标签"
android:textSize="18sp" />
</LinearLayout>
</LinearLayout>
2. ConstraintLayout 实现方式
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:id="@+id/place_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#333"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/mTvTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:background="@drawable/shape_r2_bged834d"
android:text="我是标签"
android:textColor="#ED834D"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@id/place_holder"
app:layout_constraintLeft_toRightOf="@id/tv_text"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/place_holder" />
<!--废弃: layout_constraintWidth_default="wrap" -->
<TextView
android:id="@+id/tv_text"
android:layout_width="0dp" <!--此 layout_width 需设置为 0, 否则无效果 -->
android:layout_height="wrap_content"
android:text="对外投资企业"
android:textColor="#333"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/mTvTag"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="wrap" />
layout_constraintWidth_default="wrap" is deprecated. Use layout_width="WRAP_CONTENT" and layout_constrainedWidth="true" instead.
修改为:
<TextView
android:id="@+id/tv_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="对外投资企业"
android:textColor="#333"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/mTvTag"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedWidth="true" />
</androidx.constraintlayout.widget.ConstraintLayout>
3. 右侧 TextView 固定在右侧
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:id="@+id/place_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="#333"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/mTvTag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:background="@drawable/shape_r2_bged834d"
android:text="我是标签"
android:textColor="#ED834D"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@id/place_holder"
app:layout_constraintLeft_toRightOf="@id/tv_text"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/place_holder" />
<TextView
android:id="@+id/tv_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="对外投资企业 对外投资企业 对外投资企业 对外投资企业 对外投资企业 对外投资企业 对外投资企业 "
android:textColor="#333"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/mTvTag"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
RelativeLayout 左右靠齐,不重叠
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:background="#f00"
android:text="Left Text"
android:textColor="#fff"
android:textSize="16sp" />
<!-- 多嵌套一层LinearLayout, 是为了让右侧TextView宽度自适应,否则是撑满右侧的-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_toEndOf="@id/tv_title"
android:background="#f0f"
android:gravity="end">
<TextView
android:id="@+id/tv_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00f"
android:ellipsize="end"
android:maxLines="1"
android:text="Right Text Right Text Right Text Right Text Right Text"
android:textColor="#fff"
android:textSize="16sp" />
</LinearLayout>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp">
<TextView
android:id="@+id/tv_risk_source_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:maxLines="1"
android:paddingTop="1dp"
android:textColor="#FF8C8C8C"
android:textSize="12sp"
tools:text="汇财汇资讯 · 2022-08-08" />
<!--多嵌套一层Layout,是为了子控件宽度自适应-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginEnd="6dp"
android:layout_toStartOf="@id/tv_risk_source_time">
<TextView
android:id="@+id/tv_risk_company"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_bg_r1_fff5f9fd"
android:ellipsize="end"
android:maxLines="1"
android:paddingStart="6dp"
android:paddingTop="2dp"
android:paddingEnd="6dp"
android:paddingBottom="2dp"
android:textColor="#FF1482F0"
android:textSize="12sp"
tools:text="122插上的擦拭成大事的擦飒飒的插上的擦拭超大上档次茶水单擦上档次" />
</LinearLayout>
</RelativeLayout>
Gradle 强制指定库版本
在 app module 的 build.gradle 中添加如下配置:
// 强制指定版本
configurations.all {
resolutionStrategy {
force 'androidx.core:core-ktx:1.3.1'
}
}
ViewPager 监听是否滑动到开始和结束位置
public class SwiperViewPager extends ViewPager {
OnSwiperListener mOnSwiperListener;
private float downX;
private float downY;
private boolean isTouchCaptured;
private float upX1;
private float upY1;
private float upX2;
private float upY2;
private static final int min_distance = 10;
boolean eventSent = false;
public SwiperViewPager(Context context) {
super(context);
}
public SwiperViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
try {
return super.onInterceptTouchEvent(ev);
} catch (Exception e) {
}
return false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE: {
downX = event.getX();
downY = event.getY();
if (!isTouchCaptured) {
upX1 = event.getX();
upY1 = event.getY();
isTouchCaptured = true;
} else {
upX2 = event.getX();
upY2 = event.getY();
float deltaX = upX1 - upX2;
float deltaY = upY1 - upY2;
// HORIZONTAL SCROLL
if (Math.abs(deltaX) >= Math.abs(deltaY)) {
if (Math.abs(deltaX) >= min_distance) {
// left or right
if (deltaX < 0) {
if (!eventSent && mOnSwiperListener != null) {
mOnSwiperListener.onLeftSwipe();
eventSent = true;
}
} else if (deltaX > 0) {
if (!eventSent && mOnSwiperListener != null) {
mOnSwiperListener.onRightSwipe();
eventSent = true;
}
}
}
}
}
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
isTouchCaptured = false;
eventSent = false;
}
}
return super.onTouchEvent(event);
}
public void setOnSwiperListener(OnSwiperListener listener) {
this.mOnSwiperListener = listener;
}
public interface OnSwiperListener {
void onLeftSwipe();
void onRightSwipe();
}
}
RecyclerView CheckBox 实现单选
class TimeSelectAdapter(
@LayoutRes layoutResId: Int,
list: List<String>
) : BaseQuickAdapter<String, BaseViewHolder>(layoutResId, list) {
private val mBooleanArray: SparseBooleanArray = SparseBooleanArray(list.size)
private var mCurSelectedPosition = -1 // 默认都不选中
override fun convert(holder: BaseViewHolder?, item: String) {
holder?.let {
val position = holder.layoutPosition
val checkBox: CheckBox = holder.getView(R.id.rb_time_text)
checkBox.text = item
checkBox.isChecked = mBooleanArray[position]
checkBox.setOnClickListener {
setSelectedPosition(position)
}
}
}
/**
* 设置选中的位置
*/
fun setSelectedPosition(position: Int) {
if (mCurSelectedPosition == position) {
return
}
if (mCurSelectedPosition > -1) { // 没有选中过
mBooleanArray.put(mCurSelectedPosition, false)
}
if (position > -1) {
mBooleanArray.put(position, true)
}
notifyDataSetChanged()
mCurSelectedPosition = position
}
}
ProgressBar 进度美化
/**
*
* @param max 数据源中的最大值
* @param count 当前值
*/
public void setProgress(int max, int count) {
ProgressBar progressBar = view.findViewById(R.id.pb_progress);
progressBar.setMax(max);
float start = max * 0.1f;
progressBar.setProgress(count == 0 ? 0 : (int) (start + count * 1.0f / max * (max - start)));
}
CoordinatorLayout 切换标题栏
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CoordinatorLayoutBasicActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/title_bar_height">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:padding="15dp"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="@dimen/title_bar_height">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#f00">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="title"
android:textColor="@android:color/white"
android:textSize="20sp" />
</LinearLayout>
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:background="@android:color/white"
android:visibility="gone" />
</FrameLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
HorizontalScrolllView.setOnClickListener() 无效
解决方案:
HorizontalScrollView 添加 android:fillViewport="true"属性, 然后给子View的 LinearLayout 设置 onClickListener 即可.
<HorizontalScrollView
android:id="@+id/hsv_view"
android:background="#eee"
android:fillViewport="true"
android:layout_width="match_parent"
android:layout_height="100dp">
<LinearLayout
android:id="@+id/ll_layout"
android:layout_width="wrap_content"
android:orientation="horizontal"
android:layout_height="match_parent">
<TextView
android:layout_width="1000dp"
android:layout_height="wrap_content"
android:text="TextView " />
</LinearLayout>
</HorizontalScrollView>
LinearLayout 中显示分割线
布局 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@drawable/shape_divider"
android:orientation="vertical"
android:showDividers="middle">
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="textview1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="textview2" />
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="textview3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="textview4" />
</LinearLayout>
分割线 drawable: shape_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:left="20dp">
<shape android:shape="rectangle">
<size android:height="5dp" />
<solid android:color="@color/AAD7FB" />
</shape>
</item>
</layer-list>
TextView 不同字数两端对齐
public class StringUtils {
/**
* 字符串中插入元素
*/
public static String insert(String str, String ele) {
return String.join(ele, str.split(""));
}
public static String align(String str, int length) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < length; i++) {
stringBuilder.append("问");
}
System.out.println(stringBuilder);
if (TextUtils.isEmpty(str)) {
return str;
}
if (length < 3 || length > 7) {
return str;
}
if (str.length() < 2 || str.length() > 7) {
return str;
}
if (str.length() > length) {
return str;
}
switch (length) {
case 3:
if (str.length() == 2) {
return insert(str, "\u3000");
}
break;
case 4:
if (str.length() == 2) return insert(str, "\u3000\u3000");
if (str.length() == 3) return insert(str, "\u2002");
break;
case 5:
if (str.length() == 2) return insert(str, "\u3000\u3000\u3000");
if (str.length() == 3) return insert(str, "\u3000");
if (str.length() == 4) return insert(str, "\u2004");
break;
case 6:
if (str.length() == 2) return insert(str, "\u3000\u3000\u3000\u3000");
if (str.length() == 3) return insert(str, "\u3000\u2002");
if (str.length() == 4) return insert(str, "\u2004\u2004");
if (str.length() == 5) return insert(str, "\u2005");
break;
case 7:
if (str.length() == 2) return insert(str, "\u3000\u3000\u3000\u3000\u3000");
if (str.length() == 3) return insert(str, "\u3000\u3000");
if (str.length() == 4) return insert(str, "\u2002\u2002");
if (str.length() == 5) return insert(str, "\u2002");
if (str.length() == 6) return insert(str, "\u2009");
break;
default:
break;
}
return str;
}
}
测试代码
private fun setTextViewAdjust() {
val layout = findViewById<LinearLayout>(R.id.ll_container)
val texts = "新闻公告研报经营"
for (j in 3..7) {
val stringBuilder = StringBuilder()
for (i in 0 until j) {
stringBuilder.append(texts[i])
}
val textView = TextView(this)
textView.textSize = 16f
textView.setBackgroundColor(Color.RED)
textView.text = stringBuilder
val lp = MarginLayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
lp.topMargin = 25
layout.addView(textView, lp)
var index = 2
while (index < j) {
val view = TextView(this)
view.textSize = 16f
val sb = StringBuilder()
for (i in 0 until index) {
sb.append(texts[i])
}
view.text = StringUtils.align(sb.toString(), j)
layout.addView(view)
index++
}
}
}
RecyclerView 限制 item 最大高度
// RecyclerView Adapter
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(list[position])
holder.itemView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
val height = holder.itemView.measuredHeight
val lp = holder.itemView.layoutParams
lp.height = if (height > 200) {
200
} else if (height < 150) {
150
} else {
height
}
holder.itemView.layoutParams = lp
}