縱向滾動通知 垂直跑馬燈
自定義控件ScrollNotification(extends ViewFlipper)
ViewFlipper官方解釋:Simple ViewAnimator that will animate between two or more views that have been added to it. Only one child is shown at a time. If requested, can automatically flip between each child at a regular interval. 是一個簡單的ViewAnimator,可以使添加進去的views動起來,一次只展示一個子view,可以設置固定時間間隔實現每個子view之間的自動切換。
實現思路:1.根據list向ViewFliper動態添加TextView作爲子view;2.利用ViewFliper的setInAnimation()、setOutAnimation()指定進入進出動畫來實現垂直滾動效果。
效果:
1.自定義xml屬性(Values/attrs.xml)
問題描述:允許通過layout佈局文件直接指定滾動通知內tTextView的TextSize、TextColor、SingleLine、Gravity屬性,但是ViewFlipper沒有上述屬性,需要自定義xml屬性。
實現方案:在項目values文件夾下創建attrs.xml文件(文件名隨意),根標籤爲resources,styleable名字爲ScrollNotification(一般使用控件名稱)。在這個名字裏可以定義多個屬性,例如定義了名爲mvTextSize的屬性,格式爲dimension|reference,意爲只能使用sp、dp、px、或引用R.dimens文件。其他自定義屬性類似。
<resources>
<declare-styleable name="ScrollNotification">
<attr name="mvInterval" format="integer|reference"/>
<attr name="mvTextSize" format="dimension|reference"/>
<attr name="mvTextColor" format="color|reference"/>
<attr name="mvSingleLine" format="boolean"/>
<attr name="mvGravity">
<enum name="left" value="0"/>
<enum name="center" value="1"/>
<enum name="right" value="2"/>
</attr>
</declare-styleable>
</resources>
這裏的自定義屬性的format,可以有很多種:
- reference:attr的值只能指向某一資源的ID,例如取值@id/textView。
- string:表示attr是字符串類型。
- color:attr是顏色類型,例如#ff0000,也可以使用一個指向Color的資源,比如@android:color/background_dark,但是不能用0xffff0000這樣的值。
- dimension:attr是尺寸類型,例如取值16px、16dp,也可以使用一個指向類型的資源,比如@android:dimen/app_icon_size。
- boolean:表示attr是布爾類型的值,取值只能是true或false。
- integer:表示attr是整數類型,取值只能是整數,不能是浮點數。
- float:attr是浮點數類型,取值只能是浮點數或整數。
- fraction:表示attr是百分數類型,取值只能以%結尾,例如30%、120.5%等。
- enum:表示attr是枚舉類型,在定義enum類型的attr時,可以將attr的format設置爲enum,也可以不用設置attr的format屬性,但是必須在attr節點下面添加一個或多個enum節點,這樣取值就只能去enum裏定義的值了。
- flag:表示attr是bit位標記,flag與enum有相似之處,定義了flag的attr,在設置值時,可以通過|設置多個值,而且每個值都對應一個bit位,這樣通過按位或操作符|可以將多個值合成一個值,一般在用flag表示某個字段支持多個特性。使用flag類型時,不要設置attr的format的屬性,直接在attr節點下面添加flag節點。
2.在layout佈局文件中使用
- 1.在根標籤中添加新的命名空間
爲什麼要添加新的命名空間:觀察一下layout佈局文件中各個屬性是以android:開頭的,例如android:layout_width=”match_parent”,android:是命名空間,根標籤最外層有一個xmlns:android=”http://schemas.android.com/apk/res/android”,xmlns(xmlNameSpace)的前綴爲android,也就是說所有以android:開頭的屬性都是在xmlns:android=”http://schemas.android.com/apk/res/android”命名空間之下的。要使用在步驟1中的自定義屬性,需要一個命名空間:
<1>app爲任意名稱,看個人意願,使用app爲命名空間時,直接在控件裏使用自定義屬性,eclipse會自動添加該命名空間;
<2>http://schemas.android.com/apk/res/是固定的;
<3>com.tiddler.testdemo爲當前應用包名。在eclipse環境下,可以使用當前應用包名;但是在Android Studio中因爲使用Gradle進行build,而Gradle不允許自定義的命名空間以包名結尾,在Android Studio中可以這樣定義命名空間xmlns:app=”http://schemas.android.com/apk/res-auto”,這樣定義的命名空間自動指向當前App的命名空間。):
- 2.在layout佈局文件中使用自定義控件,並使用自定義屬性
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.tiddler.demo"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#cc18A15F"
android:orientation="horizontal"
android:paddingLeft="10dp"
android:paddingRight="10dp" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<com.tiddler.myview.ScrollNotification
android:id="@+id/scrollNotification"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:flipInterval="2000"
app:mvSingleLine="true"
app:mvTextColor="#FFFFFF"
app:mvTextSize="8sp"
app:mvGravity="left"/>
</LinearLayout>
</RelativeLayout>
3.自定義ScrollNotification.java類
- 1.聲明成員變量-數據列表,並實現get()、set()方法,以便使用代碼指定數據
private Context mContext;
private List<String> notices;// 數據列表
public List<String> getNotices() {
return notices;
}
public void setNotices(List<String> notices) {
this.notices = notices;
}
- 2.創建init()方法,將layout文件中的屬性值讀出來,賦給成員變量
// 聲明各個自定義屬性對應的成員變量,並設置默認值
private int textSize = 6;// text字體大小
private int textColor = 0x000000;// text顏色
private boolean singleLine = false;// 是否單行顯示
private int gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL;
private static final int TEXT_GRAVITY_LEFT = 0, TEXT_GRAVITY_CENTER = 1,
TEXT_GRAVITY_RIGHT = 2;
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
this.mContext = context;
if (notices == null) {
notices = new ArrayList<>();
}
//通過Context的obtainStyledAttributes方法獲取TypedArray對象
/*
* 其中,TypedArray實例是個屬性的容器。AttributeSet是節點的屬性集合,在本例中是<com.tiddler.testdemo
* .myview.ScrollNotificationa節點中的屬性集合。
*/
TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
R.styleable.ScrollNotification, defStyleAttr, 0);
//在程序中獲取Layout文件中各個自定義屬性的賦值,如果沒有,則使用默認的值。
singleLine = typedArray.getBoolean(
R.styleable.ScrollNotification_mvSingleLine, false);
if (typedArray.hasValue(R.styleable.ScrollNotification_mvTextSize)) {
/*
* getDimension如果是dimen是dp或sp的單位,將其乘以density,如果是px,則不乘,返回類型爲float。
* getDimensionPixelSize總體類似,但是當單位是px時,也會乘以density,返回類型爲int。
*/
textSize = (int) typedArray.getDimensionPixelSize(
R.styleable.ScrollNotification_mvTextSize, textSize);
}
textColor = typedArray.getColor(
R.styleable.ScrollNotification_mvTextColor, textColor);
int gravityType = typedArray.getInt(
R.styleable.ScrollNotification_mvGravity, TEXT_GRAVITY_LEFT);
switch (gravityType) {
case TEXT_GRAVITY_CENTER:
gravity = Gravity.CENTER;
break;
case TEXT_GRAVITY_RIGHT:
gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL;
break;
}
//在使用完typedArray之後,要調用recycle方法回收資源
typedArray.recycle();
//設置動畫
setInAnimation(mContext, R.anim.in_scroll_notification);
setOutAnimation(mContext, R.anim.out_scroll_notification);
}
in_scroll_notification.xml文件
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromYDelta="100.0%p"
android:toYDelta="0.0" />
<alpha
android:duration="500"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>
out_scroll_notification.xml文件
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="500"
android:fromYDelta="0.0"
android:toYDelta="-100.0%p" />
<alpha
android:duration="500"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>
- 3.在構造函數中調用init()方法
public ScrollNotification(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
-4.創建createTextView(String text, int position),並將自定義屬性裏設置的值設置到TextView
/**
* 創建ViewFlipper下的TextView
*/
private TextView createTextView(String text, int position) {
TextView tv = new TextView(mContext);
tv.setGravity(gravity);
tv.setText(text);
tv.setTextColor(textColor);
tv.setTextSize(textSize);
tv.setSingleLine(singleLine);
tv.setTag(position);
return tv;
}
- 5.創建start(),併爲子View添加點擊事件
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener {
void onItemClick(int position, TextView textView);
}
/** 啓動輪播 */
public boolean start() {
if (notices == null || notices.size() == 0){
return false;
}
removeAllViews();
for (int i = 0; i < notices.size(); i++) {
final TextView textView = createTextView(notices.get(i), i);
final int finalI = i;
textView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (onItemClickListener != null) {
onItemClickListener.onItemClick(finalI, textView);
}
}
});
addView(textView);
}
if (notices.size() > 1) {
startFlipping();
}
return true;
}
- 6.創建startWithList(List notices)用以在ui線程調用
// 根據公告字符串列表啓動輪播
public void startWithList(List<String> notices) {
setNotices(notices);
start();
}
4.在Activity中使用ScrollNotification
private ScrollNotification mScrollNotification;
List<String> notices = new ArrayList<>();
notices.add("測試數據11111111");
notices.add("測試數據22222222");
notices.add("測試數據33333333");
notices.add("測試數據4444444");
notices.add("測試數據5555555");
notices.add("測試數據6666666");
mScrollNotification = (ScrollNotification) findViewById(R.id.scrollNotification);
mScrollNotification.startWithList(notices);
mScrollNotification.setOnItemClickListener(new ScrollNotification.OnItemClickListener() {
@Override
public void onItemClick(int position, TextView textView) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), textView.getText().toString(), 0).show();
}
});