Android 备忘录

1,373 阅读13分钟

highlight: androidstudio

工具&网站

手机投屏:gitee.com/Barryda/QtS…

Activity中动态设置 SoftInputMode

activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)

Activity 中动态设置 SoftInputMode 必要条件: AndroidManifest.xml <activity>中需要配置 android:WindowSoftInputMode="xxxx", 若没有配置, 则代码中如上, 达不到预期的效果

Git Push 到远程仓库后,回退到某个版本

  1. git log 查看要回退的版本号
  2. git reset --soft <版本号>
  3. git push origin 分支名(develop/master) --force 将回退结果推到远程仓库

AndroidStudio

1.AndroidStudio 自己安装的插件所在位置: www.jianshu.com/p/96daa6f99…

/Users/[username]/Library/Application Support/Google/AndroidStudio2020.3/plugins

  1. AndroidSutdio 配置目录:

/Users/xing/Library/Application Support/Google

才知道的技术点

  1. ImageView src 属性可以设置颜色值 android:src=“#ff0000”
  2. 可以将 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();
    }

start.png

自定义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> 能够减少布局层级

merge.png

relativelayout.png

startActivityForresult.png

使用 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 实现方式:

textview9.png


<?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 实现方式

789.png

<?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 固定在右侧

123.png

456.png

<?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 左右靠齐,不重叠

Snipaste_20230417_21-11-16.png

Snipaste_20230417_21-03-14.png

<?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>

81211111106.png

<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 中显示分割线

12.png

布局 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 不同字数两端对齐

21.png

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
    }