记录一些好用的开发技巧

0. 自定义一个View

现在的音乐播放器,比如天天动听或网易云音乐中都会将推荐页面中的内容进行分块,比如热门推荐、个性化推荐、最新音乐等等。每个区块前面都会有一个标题,如下图:

云音乐

虾米

可能很多人还在重用布局文件(layout文件)来做这些功能,但是如果你现在又需要在另一个(或多个)页面中使用这个标题,那么你就必须把标题的布局文件include进来,然后在代码里面findViewById来获取标题,还要设置标题TextView和标题前面小圆圈的主题什么的。这样你需要在每个使用这个标题的页面中都重复写这些代码

这样太麻烦了,你可以直接把它写成一个View,然后在布局文件中直接使用这个View

/**
 * 
 * @author bxbxbai
 * @version 1.0.0
 */
public class IndicatorView extends LinearLayout {

    TextView mTitle;

    public IndicatorView(Context context) {
        super(context);
        init();
    }

    public IndicatorView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        View view = View.inflate(getContext(), R.layout.find_song_title_bar, this);
        mTitle = (TextView) view.findViewById(R.id.id_text_title);
    }

    public void setTitle(String title) {
        mTitle.setText(title);
    }
}

1. 正方形的Layout

有很多情况会出现正方形的View,如下图:
Square Post

这样的布局一般会是最外面一个LinearLayout,包含一个RelativeLayout和一个TextView,这个RelativeLayout中包含了一个ImageView和两个TextView。如果把ImageViewlayout_height设置成wrap_content,这样肯定会有问题,有的时候图片不是正方形的,就会有各种奇形怪状。

你也可以在加载图片的时候将Bitmap的高剪成和宽一致,但是这样太麻烦了。你也可以写一个SqaureImageView,但是我觉得这样的重用性不好。

其实,最好的方式还是将这个RelativeLayout换成SquareLayout,这是一个正方形的RelativeLayout。然后将ImageViewlayout_heightlayout_width设置成match_parent就可以了,图片自动设置为正方形,再按照需求设置ImageViewscaleType

代码很少,如下:

/**
 * Created by baia on 14/10/27.
 * @author bxbxbai
 * @version 1.0.0
 */
public class SquareLayout extends RelativeLayout {

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

    public SquareLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SquareLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(0, widthMeasureSpec), getDefaultSize(0, heightMeasureSpec));

        //children are just made to fill our space
        int childWithSize = getMeasuredWidth();

        //height is set to be same as width
        heightMeasureSpec = widthMeasureSpec =
                MeasureSpec.makeMeasureSpec(childWithSize, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
}

2. LinearLayout 的divider

先来看一张图:
LinearLayout 的divider

如果要写上面图片中的布局,整体的布局肯定是两个LinearLayout,然后设置margin值让各个子View之间留出空隙。如果你设置了button_2隐藏了,然后你就看到了:

此处输入图片的描述

图片中button_1右边留出了一个margin值

如果你使用LinearLayoutandroid:divider属性就会极大的方便你的代码。android:divider的值是一个drawable,你可以定义一个如下drawable文件

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/ 
    android:shape="rectangle">
    <size
        android:width="10dp"
        android:height="10dp" />
    <solid android:color="@android:color/transparent" />
</shape>

其中,下面3个按钮的布局文件为:

<LinearLayout
    android:id="@+id/buttons_container"
    android:layout_width="match_parent"
    android:divider="@drawable/drawable_divider"
    android:showDividers="middle"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <Button
        android:id="@+id/btn_0"
        style="@style/ButtonStyle"
        android:background="@android:color/holo_purple"
        android:text="button_0" />

    <Button
        android:id="@+id/btn_1"
        style="@style/ButtonStyle"
        android:background="@android:color/darker_gray"
        android:text="button_1" />

    <Button
        android:id="@+id/btn_3"
        style="@style/ButtonStyle"
        android:background="@android:color/holo_blue_dark"
        android:text="button_2" />
</LinearLayout>

比较重要的两句代码就是:

android:divider="@drawable/drawable_divider"
android:showDividers="middle"

这个@drawable/drawable_divider就是上面写的drawable文件,showDividers的值可以选择beginning, middle, end, none,什么意思一看就明白。

LinearLayoutandroid:divider是一个非常好用而且强大的功能噢~

3. 更加方便的启动Fragment或Activity

启动Activity的一个非常普遍的方法就是

Intent intent = new Intent(MainActivity.this, AlbumActivity.class);
intent.putExtra(ALBUM_ID, 100L);
startActivity(intent);

如果app中有很多地方会启动AlbumActivity,那么这样的话你就需要在很多地方重复上面的代码,这不是在Repeat Yourself了吗。不光如此,如果一个Activity需要很多值,那么你就需要一个一个的设置,非常麻烦。

换个思路,你可以这样写:

public class AlbumActivity extends Activity {
    //....

    /**
     * launch activity
     * @param context Context
     * @param albumId id
    */
    public static void launch(Context context, long albumId) {
        Intent intent = new Intent(context, AlbumActivity.class);
        intent.putExtra(ALBUM_ID, albumId);
        context.startActivity(intent);
    }

    //...
}

然后你就可以直接这样启动AlbumActivity

AlbumActivity.launch(MainActivity.this, 100L);

其实启动Fragment也可以使用这样的方法,超级方便。

我一直觉得,我写一个Fragment或Activity是希望别人以更加方便的方式去调用,而不是别人要用我的组件的时候先要看半天代码,这样很浪费时间

4. ListView的layout_height属性

ListView的layout_height属性一般情况下不允许设置为wrap_content,这样会在ListView滚动的时候非常浪费性能(getView方法会多次调用)

5. Singleton模板

在看Android源码的时候发现这么一个类:

package android.util;

/**
 * Singleton helper class for lazily initialization.
 *
 * Modeled after frameworks/base/include/utils/Singleton.h
 *
 * @hide
 */
public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

突然发现这个代码写的真好啊啊…如果你想创建一个单例的话只要继承这个Singleton<T>模板就可以了

但是!因为Android系统源码上加了@hide标注,我们不能直接继承这个android.util.Singleton<T>类。

我们可以在工程的utils包中写一份一模一样的代码嘛~