編輯:關於Android編程
慣例,先放效果圖,DEMO在最後

想當年博主剛接觸Android的時候,看到這個效果心中只有膜拜啊,如果慢慢的自己水平也上來了,就把當年的一個想法給圓滿了吧。
好了,廢話不多說,先總結總結這個效果:
首先是需要自定義ListView,這點是必須的,然後在ListView的onTouchEvent方法中對事件進行處理普通的Item的話,是沒辦法實現這樣側滑的,即使你塞一個HorizontalScrollView進去都不行,所以也必須自定義一個ItemView實現左右側滑由於ListView的layout_width不一定是MATCH_PARENT,也可能是定值比如300dp,這個時候我們就需要建立一種機制來保證ItemView的寬度和ListView的寬度匹配,畢竟ItemView包含了兩個View,一個是正文的ContentView,一個是菜單的MenuView。 首先我從自定義ListView開始講起,這個ListView需要完成兩件事:事件分發和高度匹配。首先來看高度匹配:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
//寬度適配,改變ItemView的寬度
SlideItemView.Width = width;
for(int i = 0; i < getChildCount(); i++){
SlideItemView item = (SlideItemView) getChildAt(i);
item.resetWidth();
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
float dx = 0;
float dy = 0;
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
mTouchX = ev.getX();
mTouchY = ev.getY();
mMoveX = ev.getX();
mMoveY = ev.getY();
mTouchPosition = pointToPosition((int)ev.getX(), (int)ev.getY());
break;
case MotionEvent.ACTION_MOVE:
dx = ev.getX() - mMoveX;
dy = ev.getY() - mMoveY;
if(Math.abs(dx) > Math.abs(dy)){
//根據坐標點得到索引值
int position = pointToPosition((int)ev.getX(), (int)ev.getY());
if(mTouchPosition != ListView.INVALID_POSITION && position == mTouchPosition){
//得到內存中真實的Item
SlideItemView itemView = (SlideItemView) getChildAt(position - getFirstVisiblePosition());
itemView.scroll((int) dx);
}
}
mMoveX = ev.getX();
mMoveY = ev.getY();
break;
case MotionEvent.ACTION_UP:
dx = ev.getX() - mTouchX;
dy = ev.getY() - mTouchY;
if(Math.abs(dx) > Math.abs(dy) && Math.abs(dx) >= mTouchSlop){
int position = pointToPosition((int)ev.getX(), (int)ev.getY());
if(mTouchPosition != ListView.INVALID_POSITION && position == mTouchPosition){
//得到真正在內存中的Item
SlideItemView itemView = (SlideItemView) getChildAt(position - getFirstVisiblePosition());
//根據當前scrollX以及dx判斷是否顯示正文內容
if (itemView.shouldShowContent((int) dx)){
itemView.showContent();
}else{
itemView.showMenu();
}
}else if(position != mTouchPosition){
SlideItemView itemView = (SlideItemView) getChildAt(mTouchPosition - getFirstVisiblePosition());
//根據當前scrollX以及dx判斷是否顯示正文內容
if (itemView.shouldShowContent((int) dx)){
itemView.showContent();
}else{
itemView.showMenu();
}
}
}else{
SlideItemView itemView = (SlideItemView) getChildAt(mTouchPosition - getFirstVisiblePosition());
//根據當前scrollX以及dx判斷是否顯示正文內容
if (itemView.shouldShowContent((int) dx)){
itemView.showContent();
}else{
itemView.showMenu();
}
}
break;
case MotionEvent.ACTION_CANCEL:
if(mTouchPosition != ListView.INVALID_POSITION){
SlideItemView itemView = (SlideItemView) getChildAt(mTouchPosition - getFirstVisiblePosition());
itemView.showContent();
}
break;
}
return super.onTouchEvent(ev);
}
public void setView(SlideListView listView, int contentId, int menuId, float menuScale){
this.listView = listView;
this.content = View.inflate(getContext(), contentId, null);
this.menu = View.inflate(getContext(), menuId, null);
this.scale = menuScale;
LayoutParams param1 = new LayoutParams(Width, LayoutParams.MATCH_PARENT);
addView(content, param1);
LayoutParams param2 = new LayoutParams((int) (Width * menuScale), LayoutParams.MATCH_PARENT);
addView(menu, param2);
}
public View getContent(){
return content;
}
public View getMenu(){
return menu;
}
public void showContent(){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -mScroller.getFinalX(), 0);
invalidate();
}
public void showMenu(){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), menu.getWidth() - mScroller.getFinalX(), 0);
invalidate();
}
public boolean shouldShowContent(int dx){
//初始化
if(menu.getWidth() == 0){
resetWidth();
}
if(dx > 0){
//右滑,當滑過1/4的時候開始變化
if(mScroller.getFinalX() < menu.getWidth() * 3 / 4){
return true;
}else{
return false;
}
}else{
//左滑,當滑過1/4的時候開始變化
if(mScroller.getFinalX() < menu.getWidth() / 4){
return true;
}else{
return false;
}
}
}
public void scroll(int dx){
if(dx > 0){
//右滑
if(mScroller.getFinalX() > 0){
if(dx > mScroller.getFinalX()){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -mScroller.getFinalX(), 0);
}else{
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0);
}
}else{
mScroller.setFinalX(0);
}
invalidate();
}else{
//左滑
if(mScroller.getFinalX() < menu.getWidth()){
if(mScroller.getFinalX() - dx > menu.getWidth()){
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), menu.getWidth()- mScroller.getFinalX(), 0);
}else{
mScroller.startScroll(mScroller.getFinalX(), mScroller.getFinalY(), -dx, 0);
}
}else{
mScroller.setFinalX(menu.getWidth());
}
invalidate();
}
}
/**
* 重設寬度,在ListView的onMeasure方法中調用。
* 此方法是為了動態適配ListView的寬度,因為ListView的layout_width不一定等於MATCH_PARENT
* 也可能是定值比如300dp
*/
public void resetWidth(){
ViewGroup.LayoutParams param1 = content.getLayoutParams();
if(param1 == null){
param1 = new LayoutParams(Width, LayoutParams.MATCH_PARENT);
}else{
param1.width = Width;
}
content.setLayoutParams(param1);
ViewGroup.LayoutParams param2 = menu.getLayoutParams();
if(param2 == null){
param2 = new LayoutParams((int) (Width * scale), LayoutParams.MATCH_PARENT);
}else{
param2.width = (int) (Width * scale);
}
menu.setLayoutParams(param2);
}
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SlideListView listView = (SlideListView) findViewById(R.id.listView);
listView.setAdapter(new SlideAdapter(this, listView));
}
}
public class SlideAdapter extends BaseAdapter {
private Context context;
private SlideListView listView;
public SlideAdapter(Context context, SlideListView listView){
this.context = context;
this.listView = listView;
}
private String[] data = new String[]{
"1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231","1231231","232131231"
};
@Override
public int getCount() {
return data.length;
}
@Override
public Object getItem(int position) {
return data[position];
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
SlideItemView itemView = new SlideItemView(context);
itemView.setView(listView, R.layout.item_content, R.layout.item_menu, 2.0f / 3);
holder = new ViewHolder(itemView);
itemView.setTag(holder);
convertView = itemView;
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.textView.setText(data[position]);
final SlideItemView itemView = (SlideItemView) convertView;
holder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "點擊了imageview", Toast.LENGTH_SHORT).show();
itemView.showContent();
}
});
holder.textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "點擊了textview", Toast.LENGTH_SHORT).show();
itemView.showContent();
}
});
holder.btn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "點擊了btn1", Toast.LENGTH_SHORT).show();
itemView.showContent();
}
});
holder.btn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "點擊了btn2", Toast.LENGTH_SHORT).show();
itemView.showContent();
}
});
return convertView;
}
public class ViewHolder {
public ImageView imageView;
public TextView textView;
public TextView btn1;
public TextView btn2;
public ViewHolder(SlideItemView view){
View content = view.getContent();
imageView = (ImageView) content.findViewById(R.id.imageView);
textView = (TextView) content.findViewById(R.id.textView);
View menu = view.getMenu();
btn1 = (TextView) menu.findViewById(R.id.btn1);
btn2 = (TextView) menu.findViewById(R.id.btn2);
}
}
}
item_menu.xml:
Launcher3--抽屜
抽屜是用來放置安卓手機中所有需要顯示到Launcher上的(當然也可以進行過濾,將不想顯示的隱藏起來)應用和小部件,啟動應用、添加快捷方式到桌面、卸載等。之前也提到過,有
Android 自定義標題欄背景
設置標題欄背景1> 准備背景圖片: background_pix.png注:用背景圖片比用顏色好處,可以讓背景看起來有凹凸感.2> drawable文件夾下放
Android中使用RecyclerView實現下拉刷新和上拉加載
推薦閱讀:使用RecyclerView添加Header和Footer的方法
Android開發筆記之:Log圖文詳解(Log.v,Log.d,Log.i,Log.w,Log.e)
在Android群裡,經常會有人問我,Android Log是怎麼用的,今天我就把從網上以及SDK裡東拼西湊過來,讓大家先一睹為快,希望對大家入門Android Log有