Android進階——自定義View之組合系統控制元件實現水珠形狀的ItemView
阿新 • • 發佈:2019-02-05
引言
相信大家在專案開發的過程中一定會有不少需要在上方顯示一張圖片,而在其下方顯示提示標題的效果,作為一個介面的功能按鈕或者單純作為一個列表的item項,尤其是當這個item還需要顯示一些動畫效果時候,此時更應該當成一個整體,否則動畫效果就會需要額外的調整,否則就會不協調。
一、水珠形狀ItemView功能概述
所上圖所示,所謂ItemView本質就是一個組合控制元件————上方顯示一個圖片作為ItenView的圖示下方顯示文字作為補充說明,功能和普通的ImageViewButton大同小異,優勢在於擴充套件性更好些,客製化也更強些,可以根據不同的素材靈活組合顯示各種形狀的的ItenView,當然這樣的需求只使用系統控制元件也可以實現,只不過佈局就麻煩些和複雜些,而且處理還需要做額外的動畫效果處理,統一為一個整體優勢顯然更明顯。至於其中的成本就各自衡量,個人建議多複用。
二、水珠形狀ItemView設計思想
無論是繼承系統自有控制元件還是組合系統控制元件,核心都是重寫構造方法,在構造方法裡擴充套件自己的功能。
1、定義基本佈局
實現控制元件佈局有兩種方式,一種是通過程式碼生成,第二種是通過xml定義。
2、提供互動反饋的效果
這裡的反饋效果指的是按下的時候顯示不同的圖片和改變文字的顏色等,所以需要提供一個介面,而一切的互動源自Touch,前面的文章也說了通過重寫onTouch可以實現自定義的互動。
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction ()) {
case MotionEvent.ACTION_DOWN:
listener.onItemDown(v);
textView.setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
Log.e("TOUCHING", "onTouch: "+"fuckckck onItemDown" );
break;
case MotionEvent.ACTION _MOVE:
listener.onItemMove(v);
break;
case MotionEvent.ACTION_UP:
listener.onItemUp(v);
Log.e("TOUCHING", "onTouch: "+"fuckckck ACTION_UP" );
textView.setTextColor(getResources().getColor(android.R.color.white));
break;
case MotionEvent.ACTION_CANCEL:
Log.e("TOUCHING", "onTouch: "+"fuckckck ACTION_CANCEL" );
break;
}
return true;//return false,會不能觸發UP、MOVE操作
}
public interface OnItemTouchListener {
void onItemDown(View view);//當Item按下的時候
void onItemUp(View view);//按下之後,ACTION_UP的時候觸發
void onItemMove(View view);
}
三、實現水珠形狀ItemView
1、定義ItemView的基本佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@mipmap/bcg_btn_press">
<ImageView
android:id="@+id/item_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/twenty_four_len"
android:layout_gravity="center_horizontal"/>
<TextView
android:id="@+id/item_txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="@android:color/white"
android:textSize="@dimen/eighteen_font"
/>
</LinearLayout>
2、實現ItemView
package com.xiaoi.app.robot.view.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.xiaoi.app.robot.R;
/**
* auther: MO
* Date: 2017/1/10
* Time:15:42
* Des:
*/
public class ItemView extends RelativeLayout implements View.OnTouchListener {
private Context context;
private TextView textView;
private ImageView imageView;
private OnItemTouchListener listener;
private int imgId;
private String textId;
public ItemView(Context context) {
super(context);
this.context = context;
}
public ItemView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
}
public ItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init(attrs);
}
private void initAttr(AttributeSet attrs) {
TypedArray types = context.obtainStyledAttributes(attrs,
R.styleable.item_view);
try {
imgId = types.getResourceId(R.styleable.item_view_img_src, R.mipmap.ic_user_btn);
textId = types.getString(R.styleable.item_view_text);
} finally {
types.recycle(); // TypeArray用完需要recycle
}
}
private void init(AttributeSet attrs) {
View contentView = LayoutInflater.from(this.context).inflate(R.layout.item_view, this, true);
imageView = (ImageView) contentView.findViewById(R.id.item_img);
textView = (TextView) contentView.findViewById(R.id.item_txt);
initAttr(attrs);
setImageView(imgId);
textView.setText(textId);
setOnTouchListener(this);
}
public ImageView getImageView(){
return this.imageView;
}
public void setImageView(int resId) {
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId);
imageView.setImageBitmap(bitmap);
}
public void setTextView(String txt){
textView.setText(txt);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
listener.onItemDown(v);
textView.setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
Log.e("TOUCHING", "onTouch: "+"fuckckck onItemDown" );
break;
case MotionEvent.ACTION_MOVE:
listener.onItemMove(v);
break;
case MotionEvent.ACTION_UP:
listener.onItemUp(v);
Log.e("TOUCHING", "onTouch: "+"fuckckck ACTION_UP" );
textView.setTextColor(getResources().getColor(android.R.color.white));
break;
case MotionEvent.ACTION_CANCEL:
Log.e("TOUCHING", "onTouch: "+"fuckckck ACTION_CANCEL" );
break;
}
return true;//return false,會不能觸發UP、MOVE操作
}
public void setItemTouchListener(OnItemTouchListener listener) {
this.listener = listener;
}
public interface OnItemTouchListener {
void onItemDown(View view);//當Item按下的時候
void onItemUp(View view);//按下之後,ACTION_UP的時候觸發
void onItemMove(View view);
}
}
四、應用水珠形狀ItemView
使用起來很簡單,我就貼部分程式碼
佈局程式碼activity_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:crazymo="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/bcg_activity">
<ImageView
android:id="@+id/bcg_light"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:scaleType="centerInside"
android:src="@mipmap/bcg_main" />
<ImageView
android:id="@+id/btn_speak_bgn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:background="@mipmap/voice_01"
android:clickable="true"
android:layout_marginLeft="15dp"
android:visibility="invisible"
android:scaleType="centerInside" />
<ImageView
android:id="@+id/slide_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:clickable="true"
android:src="@drawable/selctor_slide_btn" />
<ImageView
android:id="@+id/btn_speak"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@mipmap/voice_main_default"
android:clickable="true"
android:scaleType="centerInside" />
<com.xiaoi.app.robot.view.widget.ItemView
android:id="@+id/main_user_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/one_hundred_len"
crazymo:text="@string/auth" />
<com.xiaoi.app.robot.view.widget.ItemView
android:id="@+id/main_biz_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/one_hundred_len"
crazymo:img_src="@mipmap/ic_biz"
crazymo:text="@string/biz" />
<com.xiaoi.app.robot.view.widget.ItemView
android:id="@+id/main_dance_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="@dimen/one_hundred_len"
crazymo:img_src="@mipmap/ic_dance"
crazymo:text="@string/dance"
/>
<Button
android:id="@+id/main_logout_btn"
android:layout_width="@dimen/senventy_len"
android:layout_height="@dimen/fifty_five_len"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginLeft="@dimen/one_hundred_len"
android:layout_marginBottom="@dimen/thirty_len"
android:background="@mipmap/bcg_btn_press"
android:text="@string/logout"
android:textColor="@color/white"
android:textSize="@dimen/eighteen_font"
android:visibility="visible"
/>
<com.xiaoi.app.robot.view.widget.ItemView
android:id="@+id/main_game_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/one_hundred_len"
crazymo:img_src="@mipmap/ic_game"
crazymo:text="@string/game"
/>
</RelativeLayout>
動畫xml程式碼res/animator/animator_menu_in.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="scaleX"
android:valueFrom="0"
android:valueTo="1"
android:duration="1000"
android:interpolator="@android:anim/accelerate_interpolator"
/>
<objectAnimator
android:propertyName="scaleY"
android:valueFrom="0"
android:valueTo="1"
android:duration="1000"
android:interpolator="@android:anim/accelerate_interpolator"
/>
<objectAnimator
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:duration="1000"
android:interpolator="@android:anim/accelerate_interpolator"
/>
</set>
MainActivity.java
public class MainActivity extends BaseActivity implements ItemView.OnItemTouchListener, MainView {
@Bind(R.id.bcg_light)
ImageView bcgLight;
@Bind(R.id.slide_btn)
ImageView slideBtn;
@Bind(R.id.btn_speak)
ImageView btnSpeak;
@Bind(R.id.main_user_content)
ItemView mainUserContent;
@Bind(R.id.main_biz_content)
ItemView mainBizContent;
@Bind(R.id.main_dance_content)
ItemView mainDanceContent;
@Bind(R.id.main_game_content)
ItemView mainGameContent;
private static final String TAG = "MainActivity";
@Bind(R.id.btn_speak_bgn)
ImageView btnSpeakBgn;
@Bind(R.id.main_logout_btn)
Button mainLogoutBtn;
private boolean isShow = false, isVerify = false, isSpeaking = false, needRun = true;
private int startX;
private SlidrConfig config;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int primary = getResources().getColor(R.color.colorPrimaryDark);
int secondary = getResources().getColor(R.color.colorPrimary);
config = new SlidrConfig.Builder()
.primaryColor(primary)
.secondaryColor(secondary)
.position(SlidrPosition.LEFT)
.velocityThreshold(2400)
.distanceThreshold(.25f)
.scrimStartAlpha(0.8f)
.scrimEndAlpha(0f)
.touchSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32, getResources().getDisplayMetrics()))
.build();
// Attach the Slidr Mechanism to this activity
Slidr.attach(this, config);
ButterKnife.bind(this);
init();
}
private void init() {
setItemViewListen();
showAnmail();
}
@Override
protected void onResume() {
super.onResume();
mainLogoutBtn.setVisibility(View.GONE);
}
/**
* @param v
* @param animator
*/
public void runXmlAnimSet(final View v, final int animator) {
Animator anim = AnimatorInflater.loadAnimator(this, animator);
/*Point center = ScreenUtil.getScreenCenterNoTitle(this);
if (v.getId() == R.id.main_user_content) {
v.setPivotX(center.x);
v.setPivotY(center.y);
} else if (v.getId() == R.id.main_biz_content) {
v.setPivotX(-center.x);
v.setPivotY(center.y);
} else if (v.getId() == R.id.main_dance_content) {
v.setPivotX(center.x);
v.setPivotY(-center.y);
} else if (v.getId() == R.id.main_game_content) {
v.setPivotX(-center.x);
v.setPivotY(-center.y);
}*/
anim.setTarget(v);
anim.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
if (animator == R.animator.animator_menu_in) {
v.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (animator == R.animator.animator_menu_out) {
v.setVisibility(View.GONE);
}
}
@Override
public void onAnimationCancel(Animator animation) {
return;
}
@Override
public void onAnimationRepeat(Animator animation) {
return;
}
});
anim.start();
}
private void setItemViewListen() {
mainUserContent.setItemTouchListener(this);
mainBizContent.setItemTouchListener(this);
mainDanceContent.setItemTouchListener(this);
mainGameContent.setItemTouchListener(this);
}
private void setTouchListen() {
bcgLight.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
int endX = (int) event.getX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = (int) event.getX();
Log.e("TOUCHING", "onTouch: startX" + startX + "startX" + startX);
break;
case MotionEvent.ACTION_MOVE:
//向右滑動超過50px
if (endX - startX > 50) {
startVoiceActivity();
Toast.makeText(MainActivity.this, "**Moving to RIght** ", Toast.LENGTH_SHORT).show();
} else if (startX - endX > 50) {
Log.e("TOUCHING", "onTouch:ACTION_MOVE endX" + endX + "startX:" + startX);
Toast.makeText(MainActivity.this, "**Moving to Left** ", Toast.LENGTH_SHORT).show();
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return false;
}
});
mainLogoutBtn.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
logout();
mainLogoutBtn.setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
break;
case MotionEvent.ACTION_UP:
mainLogoutBtn.setTextColor(getResources().getColor(android.R.color.white));
mainLogoutBtn.setVisibility(View.GONE);
break;
default:
break;
}
return false;
}
});
}
private void setPressBackground(View view) {
switch (view.getId()) {
case R.id.main_user_content:
if (isVerify) {
mainUserContent.setImageView(R.mipmap.ic_user_btn_press);
} else {
mainUserContent.getImageView().setImageBitmap(bitmap);
}
break;
case R.id.main_biz_content:
mainBizContent.setImageView(R.mipmap.ic_biz_press);
break;
case R.id.main_dance_content:
mainDanceContent.setImageView(R.mipmap.ic_dance_press);
break;
case R.id.main_game_content:
mainGameContent.setImageView(R.mipmap.ic_game_press);
break;
default:
break;
}
}
private void setNormalBackground(View view) {
switch (view.getId()) {
case R.id.main_user_content:
if (isVerify) {
mainUserContent.setImageView(R.mipmap.ic_user_btn);
} else {
mainUserContent.getImageView().setImageBitmap(bitmap);
}
break;
case R.id.main_biz_content:
mainBizContent.setImageView(R.mipmap.ic_biz);
break;
case R.id.main_dance_content:
mainDanceContent.setImageView(R.mipmap.ic_dance);
break;
case R.id.main_game_content:
mainGameContent.setImageView(R.mipmap.ic_game);
break;
default:
break;
}
}
private void verifyFace() {
}
private void openBiz() {
}
private void showDance() {
startActivity(new Intent(this,DanceActivity.class));
overridePendingTransition(R.anim.enter_left_to_right, R.anim.back_right_to_left);
}
private void palyGame() {
}
@OnClick({R.id.slide_btn, R.id.btn_speak, R.id.bcg_light})
void onClick(View view) {
switch (view.getId()) {
case R.id.slide_btn:
openSlide();
break;
case R.id.bcg_light:
showMenu();
break;
case R.id.btn_speak:
startSpeak();
break;
default:
break;
}
}
private void openSlide() {
startVoiceActivity();
}
private void startSpeak() {
requestionPermission();
}
private void showMenu() {
if (isShow) {
hideAnmail();
} else {
showAnmail();
}
}
//隱藏動畫
private void hideAnmail() {
runXmlAnimSet(mainUserContent, R.animator.animator_menu_out);
runXmlAnimSet(mainBizContent, R.animator.animator_menu_out);
runXmlAnimSet(mainDanceContent, R.animator.animator_menu_out);
runXmlAnimSet(mainGameContent, R.animator.animator_menu_out);
isShow = false;
}
//顯示動畫
private void showAnmail() {
runXmlAnimSet(mainUserContent, R.animator.animator_menu_in);
runXmlAnimSet(mainBizContent, R.animator.animator_menu_in);
runXmlAnimSet(mainDanceContent, R.animator.animator_menu_in);
runXmlAnimSet(mainGameContent, R.animator.animator_menu_in);
isShow = true;
}
public void logout() {
Toast.makeText(this, "**登出** ", Toast.LENGTH_SHORT).show();
mainUserContent.setImageView(R.mipmap.ic_user_btn);
mainUserContent.setTextView("身份註冊");
isVerify = false;
}
@Override
public void onItemDown(View view) {
setPressBackground(view);
switch (view.getId()) {
case R.id.main_user_content:
verifyFace();
break;
case R.id.main_biz_content:
openBiz();
break;
case R.id.main_dance_content:
showDance();
break;
case R.id.main_game_content:
palyGame();
break;
default:
break;
}
}
@Override
public void onItemUp(View view) {
setNormalBackground(view);
}
@Override
public void onItemMove(View view) {
return;
}
@Override
protected void onDestroy() {
super.onDestroy();
voiceUtil.onDestroy(false);
}
}