自定義控制元件三部曲檢視篇(四)——RecyclerView系列之一簡單使用
絕望的時候不要那麼絕望,高興的時候不要那麼高興,是你慢慢會學會的。 ——董卿
轉了一年多,又回來繼續做Android。果然還是看到程式碼最讓我興奮……但有些事,沒經歷過,總歸還是遺憾的。在VIVO的遊戲中心,有一個特別炫酷的功能:
這個功能就是使用RecyclerView來實現的,在本系列中,我們將最終制作出這樣的一個效果出來。
一、匯入Support-v7包
工欲善其事必先利其器,RecyclerView存在於support-v7包中,我們需要在新建的gradle工程中匯入support-v7包:
compile 'com.android.support:recyclerview-v7:21.0.3'
gralde 版本較高的同學,會發現compile關鍵字這裡會報警告,在高版本gradle中compile已經棄用了,改成了implementation,所以你可以改為:
implementation 'com.android.support:recyclerview-v7:21.0.3'
加上上面的依賴程式碼以後,會發現根本不好使,依賴庫根本拉不下來。這是為什麼呢?
support-v7包並不是通過maven從遠端下載的,而是通過Android Studio的SDK Manager來下載到本地,然後再引用的。本地有沒有support-v7包,大家可以看下SDK的這個位置(Sdk\extras\android\m2repository\com\android\support):
從這裡可以看到,在我的com/android/support目錄下有所有的support包,這裡也有recyclerview-v7包。如果在該資料夾下,你沒有的話,可以通過SDK Manager引入:
下載完成後,在這個資料夾下就會有對應的包存在了,當我們點進去recyclerview-v7資料夾裡面,可以看到各種版本:
大家就可以選擇這裡已有的版本引入了,比如我這裡是有25.3.1的,所以我這裡最終的引用包是:
implementation 'com.android.support:recyclerview-v7:25.3.1'
在引入support包時,需要有兩個注意事項:
- 引入的support包的版本要比targetSdkVersion要高,不然會報錯
- 如果引入了多個support包元件,它們的版本號要保持一致,不然有可能因為不是同一個版本,程式碼不配套而出現錯誤,比如我同時引入appcompat包和recyclerview包,那麼它們的寫法應該是:
implementation 'com.android.support:appcompat-v7:25.3.1'
implementation 'com.android.support:recyclerview-v7:25.3.1'
二、簡單使用
在這部分,我們首先做出一個最簡單的例子,來看下RecyclerView的使用方法。本小節所實現的效果如下圖所示:
2.1 引入RecyclerView
首先,在XML中引入RecyclerView:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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=".LinearActivity">
<android.support.v7.widget.RecyclerView
android:id="@+id/linear_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>
2.2 實現Adapter
與listView一樣,同樣需要一個Adapter來將資料和Item檢視繫結起來,但不同的的是RecyclerView的Adapter需要派生自RecyclerView.Adapter<RecyclerView.ViewHolder>
當我們寫一個Adapter的類派生自RecyclerView.Adapter<RecyclerView.ViewHolder>時,最簡單的形式是這樣的:
public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return null;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 0;
}
}
這三個函式是強制必須重寫的,其中:
- onCreateViewHolder:用於得到我們自定義的ViewHolder,在listView中,我們也會定義ViewHolder來承載檢視中的元素.
- onBindViewHolder:是用於將指定位置的資料和檢視繫結起來的
- getItemCount:用於獲取列表總共的item數
可見,這三項其實在listview中也都是需要做的,只是這裡單獨通過回撥給列出來了,我們只需要補充上這三個函式,就算實現了Adapter了.
在填充RecyclerAdapter之前,我們知道,一般而言ListView的資料都是從外部傳進來的,所以我們需要給RecyclerAdapter新增上一個建構函式,將資料從外部傳進來:
private Context mContext;
private ArrayList<String> mDatas;
public RecyclerAdapter(Context context, ArrayList<String> datas) {
mContext = context;
mDatas = datas;
}
為了方便起來,我們傳進來的資料非常簡單,就是一個String字串,同時,由於在RecyclerAdater中,經常會用到Context,所以我們也把Context傳進來,並且儲存起來.
接下來,我們就是先建立一個HolderView,然後填充那三個函式。我們都知道HolderView主要是為了儲存每一個Item的檢視的控制元件元素。所以我們要先建立一個Item的xml(item_layout.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="wrap_content">
<TextView
android:id="@+id/item_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp" />
</LinearLayout>
在這個item中,只有一個TextView,所以我們先寫一個ViewHolder,ViewHolder的主要作用就是將XML中的控制元件以變數的形式儲存起來,方便我們後面資料繫結.
public class NormalHolder extends RecyclerView.ViewHolder{
public TextView mTV;
public NormalHolder(View itemView) {
super(itemView);
mTV = (TextView) itemView.findViewById(R.id.item_tv);
mTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext,mTV.getText(),Toast.LENGTH_SHORT).show();
}
});
}
}
在這裡,在建立ViewHolder時,將整個ItemView傳了進來,然後將TextView從ItemView中取出來儲存在mTV變數中.並且,在點選mTV後,彈出這個TextView的內容.
在寫好ViewHolder以後,我們就要逐個填充RecyclerAdapter的三個函數了。首先是onCreateViewHolder:
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
return new NormalHolder(inflater.inflate(R.layout.item_layout,parent,false));
}
在每一次需要建立ViewHolder時,都會呼叫onCreateViewHolder函式,所以我們需要在onCreateViewHolder中返回我們建立的ViewHolder例項。
然後在onBindViewHolder中,將資料與ViewHolder繫結起來:
public void onBindViewHolder(ViewHolder holder, int position) {
NormalHolder normalHolder = (NormalHolder)holder;
normalHolder.mTV.setText(mDatas.get(position));
}
最後,在getItemCount中返回資料的個數:
public int getItemCount() {
return mDatas.size();
}
到這裡,整個RecyclerAdapter就實現完了,完整的程式碼如下,供大家參考:
public class RecyclerAdapter extends RecyclerView.Adapter<ViewHolder> {
private Context mContext;
private ArrayList<String> mDatas;
public RecyclerAdapter(Context context, ArrayList<String> datas) {
mContext = context;
mDatas = datas;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
return new NormalHolder(inflater.inflate(R.layout.item_layout, parent, false));
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
NormalHolder normalHolder = (NormalHolder) holder;
normalHolder.mTV.setText(mDatas.get(position));
}
@Override
public int getItemCount() {
return mDatas.size();
}
public class NormalHolder extends RecyclerView.ViewHolder {
public TextView mTV;
public NormalHolder(View itemView) {
super(itemView);
mTV = (TextView) itemView.findViewById(R.id.item_tv);
mTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
2.3 填充RecyclerView
之後,回到Activity中,首先,構造一個模擬資料的函式,用於填充RecyclerVIew:
public class LinearActivity extends AppCompatActivity {
…………
private ArrayList<String> mDatas =new ArrayList<>();
private void generateDatas(){
for (int i=0;i<200;i++){
datas.add("第 " + i +" 個item");
}
}
}
之後,在OnCreate函式中填充RecyclerView:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear);
generateDatas();
RecyclerView mRecyclerView = (RecyclerView)findViewById(R.id.linear_recycler_view);
//線性佈局
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(linearLayoutManager);
RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
}
這裡與listVIew唯一的區別是,這裡需要設定一個LayoutManager物件,這裡設定的是LinearLayoutManager,也就是垂直列表。我們知道Adapter的職責是用資料將每個Item的控制元件填充起來。而RecycerView與ListView不一樣的是,它不僅能實現傳統的滑動的列表,還能實現GridView和瀑布流造型,或者其它各式各樣的特殊造型。而這些造型的實現就是通過LayoutManger來實現的,我們通過Adapter將Item填充了以後,那每個Item怎麼擺放是由誰來做的呢?擺放Item的操作就是使用LayoutManager來實現出來的。所以每個LayoutManger所實現的擺放Item的方式都是不一樣的,比如:LinearLayoutManager就是傳統的ListView的功能,上下滾動或者左右滾動。而GridLayoutManager則是網格擺放,而StaggeredGridLayoutMnager則是瀑布流擺放。
到這裡,我們就實現了本部分開頭的上下滾動的效果了。
三、其它LayoutManager
從上面的分析可以看出,擺放Item的操作主要是由LayoutManager來實現的,這也就是RecyclerView可以製作出各種特殊列表樣式的原因。系統為我們提供了幾個已經寫好的LayoutManager:
其中WearableLinearLayoutManager用於在穿戴裝置上使用,比如智慧手錶等,所以我們這裡不討論它。下面我們逐個看下這些LayoutManger所實現的效果。
3.1 GridLayoutManager
我們知道了,LayoutManger的職責就是如何擺放Item,所以對於Adapter與RecyclerView是沒有影響的,除非我們為了迎合LayoutManger要改Item的佈局,比如為了實現瀑布流效果而需要改變每個Item的寬或高等。一般而言,我們更改LayoutManager,不需要對其它物件操作。所以這也是RecyclerView比較好的一個地方,通過RecycerView本身,Adapter,LayoutManger實現了完全解耦。各自實現各自的功能,與其它部分無關。而GridLayoutManager的主要作用就是將Item進行網格狀擺放,進而實現網格佈局效果。
所以我們要設定GridLayoutManager時,也只需要更改Acitivity中的設定LayoutManager這塊程式碼即可,其它都不需要動:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid);
generateDatas();
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.grid_recycler_view);
//如果是橫向滾動,後面的數值表示的是幾行,如果是豎向滾動,後面的數值表示的是幾列
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 5);
gridLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(gridLayoutManager);
RecyclerAdapter adapter = new RecyclerAdapter(this, mDatas);
mRecyclerView.setAdapter(adapter);
}
private void generateDatas() {
for (int i = 0; i < 200; i++) {
mDatas.add("第 " + i + " 個item");
}
}
其中:
public GridLayoutManager(Context context, int spanCount)
- spanCount:如果是豎向滾動,則表示當前劃分為幾列;如果是橫向滾動,則表示當前劃分為幾行。
效果如下:
可以看到,這裡就實現了網格效果,並且是上下滾動。我們通過gridLayoutManager.setOrientation();
可以設定RecyclerView的滾動方向,取值有LinearLayoutManager.VERTICAL和LinearLayoutManager.HORIZONTAL
如果我們將它改為橫向滾動:
GridLayoutManager gridLayoutManager = new GridLayoutManager(this,5);
gridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
mRecyclerView.setLayoutManager(gridLayoutManager);
效果如下:
在橫向滾動的情況下,列表就變成了五行了。
3.2 StaggeredGridLayoutMnager
StaggeredGridLayoutMnager主要用來實現瀑布流效果。同樣,我們直接把LayoutManager改為StaggeredGridLayoutMnager:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_staggered);
generateDatas();
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.stagger_recycler_view);
//瀑布流佈局
StaggeredGridLayoutManager staggeredManager = new StaggeredGridLayoutManager(5, StaggeredGridLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(staggeredManager);
……
}
其中 public StaggeredGridLayoutManager(int spanCount, int orientation)
- spanCount:同樣表示行數或列數,如果是豎向滾動,則表示當前劃分為幾列;如果是橫向滾動,則表示當前劃分為幾行。
- orientation:表示滾動方向,取值有:StaggeredGridLayoutManager.HORIZONTAL和StaggeredGridLayoutManager.VERTICAL
下面來看下效果:
可以看到,這裡由於每個Item的高度是一定的,所有的Item的高度都一樣,導致所實現的瀑布流佈局跟網格佈局完全相同,所以如果想實現瀑布流佈局,那必然需要每個Item的高度是不一樣的。
所以我們需要修改Adapter,在程式碼中動態設定每個Item的高度,讓每個Item的高度儘量都不一樣,這樣就可以看到瀑布流效果了。
所以,主要修改了這兩個地方:
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
NormalHolder normalHolder = (NormalHolder)holder;
normalHolder.mTV.setText(mDatas.get(position));
ViewGroup.LayoutParams lp = normalHolder.mTV.getLayoutParams();
lp.height = getRandomHeight();
normalHolder.mTV.setLayoutParams(lp);
}
private int getRandomHeight(){
int randomHeight = 0;
do{
randomHeight = (int)(Math.random()*300);
}while (randomHeight == 0);
return randomHeight;
}
定義了一個getRandomHeight()函式,得到一個0-300之間的一個數值。然後在onBindViewHolder中,將這個數值設定給TextView,作為TextVIew的高度。
完整的改寫後的Adapter的程式碼如下:
public class StaggerRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private ArrayList<String> mDatas;
public StaggerRecyclerAdapter(Context context, ArrayList<String> datas) {
mContext = context;
mDatas = datas;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
return new NormalHolder(inflater.inflate(R.layout.item_layout, parent, false));
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
NormalHolder normalHolder = (NormalHolder) holder;
normalHolder.mTV.setText(mDatas.get(position));
ViewGroup.LayoutParams lp = normalHolder.mTV.getLayoutParams();
lp.height = getRandomHeight();
normalHolder.mTV.setLayoutParams(lp);
}
private int getRandomHeight() {
int randomHeight = 0;
do {
randomHeight = (int) (Math.random() * 300);
} while (randomHeight == 0);
return randomHeight;
}
@Override
public int getItemCount() {
return mDatas.size();
}
public class NormalHolder extends RecyclerView.ViewHolder {
public TextView mTV;
public NormalHolder(View itemView) {
super(itemView);
mTV = (TextView) itemView.findViewById(R.id.item_tv);
mTV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, mTV.getText(), Toast.LENGTH_SHORT).show();
}
});
}
}
}
改寫後的效果如下:
首先,可以看到,這裡已經實現了瀑布流效果,但是每當滾動到頂部時,所有的Item會跳動一下,重新佈局,這是為什麼呢?
這是因為在每次拉到頂部的時候,所有的Item會重新執行一次onBindViewHolder函式,因為我們Item的高度就是在這個函式中隨機生成的,所以在拉到頂部時,每個Item的高度就會重新生成,造成的結果就是看起來跳了一下,重新佈局了。
所以,要解決這個問題也比較簡單,那就是用一個數組,在Adapter初始化的時候,就把每個Item的高度生成好,然後在onBindViewHolder中直接取出即可。
所以,我們可以先申請一個數組,並且在Adapter初始化時,儲存每個Item的高度
public class StaggeredRecyclerAdapter extends Adapter<ViewHolder> {
private Context mContext;
private ArrayList<String> mDatas;
private ArrayList<Integer> mHeights = new ArrayList<>();
public StaggeredRecyclerAdapter
相關推薦
自定義控制元件三部曲檢視篇(四)——RecyclerView系列之一簡單使用
絕望的時候不要那麼絕望,高興的時候不要那麼高興,是你慢慢會學會的。 ——董卿
轉了一年多,又回來繼續做Android。果然還是看到程式碼最讓我興奮……但有些事,沒經歷過,總歸還是遺憾的。在VIVO的遊戲中心,有一個特別炫酷的功能:
這個功能就是使
自定義控制元件三部曲檢視篇(五)——RecyclerView系列之二ItemDecoration
從來不跌倒不算光彩,每次跌倒後能再站起來,才是最大的榮耀。
一、新增分割線
1.1 引入ItemDecoration
在上一篇中,我們講解了RecyclerView的基本使用方法,但有個問題:為什麼Item之間沒有分割線呢?其實,給RecyclerView新
自定義控制元件三部曲檢視篇(三)——瀑布流容器WaterFallLayout實現
前言:只要在前行,夢想就不再遙遠
系列文章:
前面兩節講解了有關ViewGroup的onMeasure、onLayout的知識,這節我們深入性地探討一下,如何實現經常見到的瀑布流容器,本節將實現的效果圖如下:
從效果圖中可以看出這裡要完成的
自定義控制元件之繪圖篇(四):canvas變換與操作
前言:前幾篇講解了有關canvas繪圖的一些操作,今天更深入一些,講講對畫布的操作,這篇文章不像前幾篇那麼容易理解,如果以前沒有接觸過畫布的童鞋可能比較難以理解,為什麼會這樣。我儘量多畫圖,讓大家更清晰明白。前幾天偶然看到一篇文章,寫的樸實無華,充滿正能量,我非常喜歡裡面的一
自定義控制元件之繪圖篇(三):區域(Range)
前言:最近幾天對畫圖的研究有些緩慢,專案開始寫程式碼了,只能在晚上空閒的時候捯飭一下自己的東西,今天給大家講講區域的相關知識,已經想好後面兩篇的內容了,這幾天有時間趕緊寫出來給大家。有關介面開發的東東內容確實比較多,慢慢來吧,總有一天會不一樣。我自己的一句警言,送給大家:想要
C#自定義控制元件程式設計輕鬆入門(1)
前 言
話說,許多新手在接觸C#的時候都覺得C#使用起來特別容易方便,相對C++來說沒有那麼多的繁瑣,比如C++每次在使用一個函式,都要先在標頭檔案中宣告一遍,而C#宣告和實現都在一起,立馬可以用。而且不會一會要寫指標一會兒要寫引用,如果是遇到VC那些控制代碼就把頭給搞大。
隨著
(四十九)c#Winform自定義控制元件-下拉框(表格)
前提
入行已經7,8年了,一直想做一套漂亮點的自定義控制元件,於是就有了本系列文章。
GitHub:https://github.com/kwwwvagaa/NetWinformControl
碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr
自定義控制元件三部曲之繪圖篇(六)——Path之貝賽爾曲線和手勢軌跡、水波紋效果
前言:好想義無反顧地追逐夢想從這篇開始,我將延續androidGraphics系列文章把圖片相關的知識給大家講完,這一篇先稍微進階一下,給大家把《android Graphics(二):路徑及文字》略去的quadTo(二階貝塞爾)函式,給大家補充一下。 本篇最終將以兩個例子給
自定義控制元件三部曲之動畫篇(一)——alpha、scale、translate、rotate、set的xml屬性及用法
前言:這幾天做客戶回訪,感觸很大,使用者只要是留反饋資訊,總是一種恨鐵不成鋼的心態,想用你的app,卻是因為你的技術問題,讓他們不得不放棄,而你一個回訪電話卻讓他們盡釋前嫌,當最後把手機號留給他們以便隨時溝通的時候,總會發來一條條的鼓勵簡訊,讓我不自主的開始內疚。哎,多麼可愛
自定義控制元件三部曲之繪圖篇(十三)——Canvas與圖層(一)
前言:猛然知道姥姥79了,我好怕,好想哭系列文章:一、如何獲得一個Canvas物件方法一:自定義view時, 重寫onDraw、dispatchDraw方法(1)、定義 我們先來看一下onDraw、dispatchDraw方法的定義protected void onDraw(
自定義控制元件三部曲之動畫篇(二)——Interpolator插值器
前言:雖然我不太能欣賞的了帕爾哈提的音樂,但我確實很欣賞他的人生態度,專心做自己,不想名利得失,有一天,你想要的東西都會來。其實我覺得,人生最可怕的就是停止不前,只要一直前行,總有一天會到達人生巔峰。相關文章:一、概述Interpolator屬性是Animation類的一個X
自定義控制元件三部曲之動畫篇(十一)——layoutAnimation與gridLayoutAnimation
前言:人或許天生是懶惰的,明知道的不足,卻不努力彌補。
前幾篇給大家講述瞭如何針對某一個控制元件應用動畫,這篇將給大家講解如何給容器中的控制元件應用統一動畫。即在容器中控制元件出現時,不必為每個控制元件新增進入動畫,可以在容器中為其新增統一的進入和
自定義控制元件三部曲之繪圖篇(二十)——RadialGradient與水波紋按鈕效果
前言:每當感嘆自己的失敗時,那我就問你,如果讓你重新來一次,你會不會成功?如果會,那說明並沒有拼盡全力。
系列文章:
最近博主實在是太忙了,部落格更新實在是太慢了,真是有愧大家。
這篇將是Shader的最後一篇,下部分,我們將講述Canvas變
自定義控制元件三部曲之動畫篇(十)——聯合動畫的XML實現與使用示例
前言:不畏人生,或許才能方得始終;大膽拼,大膽闖是要有一定資本的,在能力不到的時候,就只有選擇忍氣吞聲!
上篇給大家講了有關AnimatorSet的程式碼實現方法,這篇我們就分別來看看如何利用xml來實現ValueAnimator、ObjectAn
自定義控制元件 輪盤 來源於GITHUB(記錄,筆記)
自定義控制元件:輪盤抽獎
-------邏輯程式碼(輪盤的類)首先要寫一個類繼承SurfaceView 實現Callback和Runnable方法:
//所使用的包
import android.content.Context;
import android.graphics.Ca
自定義控制元件 輪盤 來源於GITHUB(記錄,筆記)
自定義控制元件:輪盤抽獎
-------邏輯程式碼(輪盤的類)首先要寫一個類繼承SurfaceView 實現Callback和Runnable方法:
//所使用的包
import android.content.Context;
import android.
Android自定義控制元件--圓形進度條(中間有圖diao)
智慧家居越來越流行,在智慧家居中我們常要表現一些資料的百分比 圓形度條中間加個圖是一種非常流行的自定義View
1.第一步 你首先需要對類進行繼承View
public class CircleProgressImageView extends View
2.第二步 要實
Android自定義控制元件之入門篇---整理網路上的資源
前言,
我的視訊系列 http://edu.csdn.net/course/detail/2741,
一起來學習Android…
本篇部落格主要是想要講解一下自定義控制元件如何入門,其中有好多資料資源來源自網路,綜合了網路上一些有些的博文
自定義控制元件之繪圖篇:Canvas與圖層(二)
public class CLIP_TO_LAYER_SAVE_FLAG_VIEW extends View {
private Paint mPaint;
public CLIP_TO_LAYER_SAVE_FLAG_VIEW(Context context, Att
Qt編寫自定義控制元件37-發光按鈕(會呼吸的痛)
一、前言
這個控制元件是好早以前寫的,已經授權過好幾個人開源過此控制元件程式碼,比如紅磨坊小胖,此控制元件並不是來源於真實需求,而