自定義ViewGroup實現(xiàn)微信朋友圈九宮格圖片控件

簡介

最近項目里有個類似微信朋友圈的九圖控件的需求,Github找了一下揩环,發(fā)現(xiàn)都不太滿足需求斜纪,我需要單張圖片的時候可以按照圖片寬高比列在一定范圍內(nèi)自適應颁褂,而大多開源項目單張圖片也是一個小正方形,所以傀广,干脆自己動手寫一個

項目源碼

具體自定義NineImageLayout過程,可以查看NineImageLayout

效果圖如下:
ezgif.com-crop.gif
主要功能如下:
  • 1:單張圖片的時候支持按照圖片寬高比列在設(shè)定區(qū)域內(nèi)自適應
  • 2:Adapter方式綁定數(shù)據(jù)和UI
  • 3:圖片點擊事件回調(diào)
  • 4:設(shè)置圖片間隔大小
  • 5:自由通過Glide設(shè)置ImageView圓角效果
使用:
  • 1:自定義屬性如下
<resources>
    <declare-styleable name="NineImageLayout">
        <!-- 控件寬高 -->
        <attr name="nine_layoutWidth" format="dimension"/>
        <!-- 單張圖片時的最大寬高范圍-->
        <attr name="nine_singleImageWidth" format="dimension" />
        <!-- 圖片之間間隙大小 -->
        <attr name="nine_imageGap" format="dimension" />
    </declare-styleable>
</resources>
  • 2:布局中使用自定義NineImageLayout
 <com.cyq.customview.nineLayout.view.NineImageLayout
        android:id="@+id/nine_image_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_title"
        android:layout_marginTop="20dp"
        app:nine_imageGap="4dp"
        app:nine_layoutWidth="300dp"
        app:nine_singleImageWidth="180dp" />
  • 3:Adapter方式綁定數(shù)據(jù)和UI,其中Glide.asBitmap是為了計算圖片寬高彩届,如果后臺有返回圖片的寬高可以省略這一步伪冰,直接setSingleImage(width, height,imageView),
    Ps:如果可以建議后臺返回圖片寬高樟蠕,這樣可以避免單張圖片的時候控件高度跳屏贮聂,比如我限制單張圖片寬高在200dp范圍,要展示寬1000px高500px的時候寨辩,在圖片未加載完成時控件寬高為200dp吓懈,圖片加載完成后高度變?yōu)?00dp,會有一個不好的用戶體驗靡狞,所以建議上傳圖片的時候記錄圖片寬高信息
nineImageLayout.setAdapter(new NineImageAdapter() {
            @Override
            protected int getItemCount() {
                return mData.size();
            }

            @Override
            protected View createView(LayoutInflater inflater, ViewGroup parent, int i) {
                return inflater.inflate(R.layout.item_img_layout, parent, false);
            }

            @Override
            protected void bindView(View view, final int i) {
                final ImageView imageView = view.findViewById(R.id.iv_img);
                Glide.with(mContext).load(mData.get(i)).into(imageView);
                if (mData.size() == 1) {
                    Glide.with(mContext)
                            .asBitmap()
                            .load(mData.get(0))
                            .into(new SimpleTarget<Bitmap>() {
                                @Override
                                public void onResourceReady(Bitmap bitmap, Transition<? super Bitmap> transition) {
                                    final int width = bitmap.getWidth();
                                    final int height = bitmap.getHeight();
                                    nineImageLayout.setSingleImage(width, height,imageView);
                                }
                            });
                    Glide.with(mContext).load(mData.get(0)).into(imageView);
                } else {
                    Glide.with(mContext).load(mData.get(i)).into(imageView);
                }
            }

            @Override
            public void OnItemClick(int i, View view) {
                super.OnItemClick(position, view);
                Toast.makeText(mContext, "position:" + mData.get(i), Toast.LENGTH_SHORT).show();
            }
        });
列表里面使用
  • 1: 頁面放一個RecyclerView
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".nineLayout.NineImageLayoutActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</FrameLayout>
  • 2:item布局如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_margin="20dp">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="標題"
        android:textColor="@android:color/black"
        android:textSize="18sp" />

    <com.cyq.customview.nineLayout.view.NineImageLayout
        android:id="@+id/nine_image_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv_title"
        android:layout_marginTop="20dp"
        app:nine_imageGap="4dp"
        app:nine_layoutWidth="300dp"
        app:nine_singleImageWidth="180dp" />
</RelativeLayout>
  • 3:Activity中構(gòu)造一下測試數(shù)據(jù)耻警,大致代碼如下
public class NineImageLayoutActivity extends AppCompatActivity {
    private RecyclerView mRecyclerView;
    private MyAdapter mAdapter;
    private Random random;
    private final String URL_IMG = "http://q3x62hkt1.bkt.clouddn.com/banner/58f57dfa5bb73.jpg";
    private final String URL_IMG_2 = "http://q3x62hkt1.bkt.clouddn.com/timg.jpeg";
    private List<List<String>> mList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_nine_image_layout);
        random = new Random();
        List<String> testList = new ArrayList<>();
        testList.add(URL_IMG_2);
        for (int i = 0; i < 100; i++) {
            int count = i % 9 + 1;
            List<String> list = new ArrayList<>();
            for (int j = 0; j < count; j++) {
                list.add(URL_IMG);
            }
            if (i % 8 == 0) {
                mList.add(testList);
            }
            mList.add(list);
        }
        mRecyclerView = findViewById(R.id.recyclerview);
        mAdapter = new MyAdapter(mList, this);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(mAdapter);
    }
}
  • 4: MyAdapter中設(shè)置數(shù)據(jù)
import java.util.List;

/**
 * @author : ChenYangQi
 * date   : 2020/1/16 13:49
 * desc   :
 */
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
    private List<List<String>> mList;
    private Context mContext;

    public MyAdapter(List<List<String>> mList, Context mContext) {
        this.mList = mList;
        this.mContext = mContext;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_nine_img_layout_list, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
        final List<String> mData = mList.get(position);
        holder.tvTitle.setText("這是" + mData.size() + "張圖片的標題");
        final NineImageLayout nineImageLayout = holder.nineImageLayout;
        holder.nineImageLayout.setAdapter(new NineImageAdapter() {
            @Override
            protected int getItemCount() {
                return mData.size();
            }

            @Override
            protected View createView(LayoutInflater inflater, ViewGroup parent, int i) {
                return inflater.inflate(R.layout.item_img_layout, parent, false);
            }

            @Override
            protected void bindView(View view, final int i) {
                final ImageView imageView = view.findViewById(R.id.iv_img);
                Glide.with(mContext).load(mData.get(i)).into(imageView);
                if (mData.size() == 1) {
                    Glide.with(mContext)
                            .asBitmap()
                            .load(mData.get(0))
                            .into(new SimpleTarget<Bitmap>() {
                                @Override
                                public void onResourceReady(Bitmap bitmap, Transition<? super Bitmap> transition) {
                                    final int width = bitmap.getWidth();
                                    final int height = bitmap.getHeight();
                                    nineImageLayout.setSingleImage(width, height,imageView);
                                }
                            });
                    Glide.with(mContext).load(mData.get(0)).into(imageView);
                } else {
                    Glide.with(mContext).load(mData.get(i)).into(imageView);
                }
            }

            @Override
            public void OnItemClick(int i, View view) {
                super.OnItemClick(position, view);
                Toast.makeText(mContext, "position:" + mData.get(i), Toast.LENGTH_SHORT).show();
            }
        });
    }

    @Override
    public int getItemCount() {
        return mList.size();
    }
 
    class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvTitle;
        NineImageLayout nineImageLayout;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvTitle = itemView.findViewById(R.id.tv_title);
            nineImageLayout = itemView.findViewById(R.id.nine_image_layout);
        }
    }
}

Github源碼地址

具體自定義NineImageLayout過程,可以查看NineImageLayout

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末甸怕,一起剝皮案震驚了整個濱河市甘穿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌梢杭,老刑警劉巖温兼,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異武契,居然都是意外死亡募判,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門咒唆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來届垫,“玉大人,你說我怎么就攤上這事钧排《厍唬” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵恨溜,是天一觀的道長符衔。 經(jīng)常有香客問我找前,道長,這世上最難降的妖魔是什么判族? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任躺盛,我火速辦了婚禮,結(jié)果婚禮上形帮,老公的妹妹穿的比我還像新娘槽惫。我一直安慰自己,他們只是感情好辩撑,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布界斜。 她就那樣靜靜地躺著,像睡著了一般合冀。 火紅的嫁衣襯著肌膚如雪各薇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天君躺,我揣著相機與錄音峭判,去河邊找鬼。 笑死棕叫,一個胖子當著我的面吹牛林螃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俺泣,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼疗认,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了伏钠?” 一聲冷哼從身側(cè)響起侮邀,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贝润,沒想到半個月后绊茧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡打掘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年华畏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尊蚁。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡亡笑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出横朋,到底是詐尸還是另有隱情仑乌,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站晰甚,受9級特大地震影響衙传,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜厕九,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一蓖捶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扁远,春花似錦俊鱼、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至谷羞,卻和暖如春焙蚓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洒宝。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留萌京,地道東北人雁歌。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像知残,于是被迫代替她去往敵國和親靠瞎。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容