Android仿外賣購(gòu)物車功能
本文實(shí)例為大家分享了Android實(shí)現(xiàn)外賣購(gòu)物車功能的具體代碼,供大家參考,具體內(nèi)容如下
先看看效果圖:

知識(shí)點(diǎn)分析
效果圖來看不復(fù)雜內(nèi)容并沒多少,值得介紹一下的知識(shí)點(diǎn)也就下面幾個(gè)吧
- 列表標(biāo)題懸停
- 左右列表滑動(dòng)時(shí)聯(lián)動(dòng)
- 添加商品時(shí)的拋物線動(dòng)畫
- 底部彈出購(gòu)物車清單
- 數(shù)據(jù)的同步
另外就是實(shí)現(xiàn)效果的時(shí)候可能會(huì)遇到的幾個(gè)坑。。。
布局很簡(jiǎn)單直接進(jìn)入代碼
1:列表標(biāo)題懸停
現(xiàn)在做項(xiàng)目列表什么的基本拋棄了ListView改用RecyclerView,上篇博客中的標(biāo)題懸停也是使用了一個(gè)RecyclerView的開源項(xiàng)目sticky-headers-recyclerview,不過寫這個(gè)demo的時(shí)候遇到了兩個(gè)坑
1)、sticky-headers-recyclerview做懸停標(biāo)題的時(shí)候scrollToPosition(int position)方法滾動(dòng)的位置不準(zhǔn)確。
2)、當(dāng)布局復(fù)雜點(diǎn)的時(shí)候 如果RecyclerView的寬度自適應(yīng)或者使用權(quán)重百分比之類可能會(huì)導(dǎo)致header顯示空白。
并且該開源項(xiàng)目作者已經(jīng)停止維護(hù),所以這次又換回了StickyListHeadersListView。
需要購(gòu)物車Demo的很多都是新手,這里簡(jiǎn)單介紹下StickyListHeadersListView的使用
1)、AS引用 gradle文件dependencies內(nèi)添加
compile 'se.emilsjolander:stickylistheaders:2.7.0'
2)、xml文件中使用StickyListHeadersListView代替ListView
<se.emilsjolander.stickylistheaders.StickyListHeadersListView android:layout_width="match_parent" android:background="#fff" android:id="@+id/itemListView" android:layout_height="match_parent"> </se.emilsjolander.stickylistheaders.StickyListHeadersListView>
3)、Adapter繼承BaseAdapter和接口StickyListHeadersAdapter
StickyListHeadersAdapter接口包括兩個(gè)方法
View getHeaderView(int position, View convertView, ViewGroup parent); long getHeaderId(int position);
代碼中使用和ListView一樣,下面是幾個(gè)特有的方法,看方法名也很容易理解用途
public void setAreHeadersSticky(boolean areHeadersSticky);
public boolean areHeadersSticky();
public void setOnHeaderClickListener(OnHeaderClickListener listener);
public interface OnHeaderClickListener {
public void onHeaderClick(StickyListHeadersListView l, View header, int itemPosition, long headerId, boolean currentlySticky);
}
public void setOnStickyHeaderChangedListener(OnStickyHeaderChangedListener listener);
public interface OnStickyHeaderChangedListener {
void onStickyHeaderChanged(StickyListHeadersListView l, View header, int itemPosition, long headerId);
}
public View getListChildAt(int index);
public int getListChildCount();
2:左右列表聯(lián)動(dòng)
聯(lián)動(dòng)主要有兩個(gè)效果
- 左側(cè)列表點(diǎn)擊選擇分類,右側(cè)列表滑動(dòng)到對(duì)應(yīng)分類
- 右側(cè)列表滑動(dòng)過程中左側(cè)列表高亮的分類跟隨變化
第一個(gè)效果簡(jiǎn)單,左側(cè)列表item添加點(diǎn)擊事件,事件中調(diào)用右側(cè)列表的setSelection(int positon) 方法。
第二個(gè)效果要給右側(cè)列表添加ScrollListener,根據(jù)列表中顯示的第一條數(shù)據(jù)設(shè)置左側(cè)選中的分類
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//根據(jù)firstVisibleItem獲取分類ID,根據(jù)分類id獲取左側(cè)要選中的位置
GoodsItem item = dataList.get(firstVisibleItem);
if(typeAdapter.selectTypeId != item.typeId) {
typeAdapter.selectTypeId = item.typeId;
typeAdapter.notifyDataSetChanged();
//左側(cè)列表是個(gè)RecyclerView 所以使用smoothScrollToPosition(int position) 使當(dāng)對(duì)應(yīng)position的item可以滾動(dòng)顯示出來
rvType.smoothScrollToPosition(int position)(getSelectedGroupPosition(item.typeId));
}
}
});
3:添加商品的動(dòng)畫
添加商品一共有三個(gè)動(dòng)畫
- 當(dāng)商品從0到1 旋轉(zhuǎn)左移顯示出減號(hào)按鈕
- 當(dāng)商品從1到0 減號(hào)按鈕旋轉(zhuǎn)右移消失
- 添加商品時(shí)拋物線動(dòng)畫添加到購(gòu)物車圖標(biāo)
前兩個(gè)動(dòng)畫很簡(jiǎn)單可以分解成三個(gè)補(bǔ)間動(dòng)畫 旋轉(zhuǎn)、平移、透明度。
可以用xml完成,也可以代碼設(shè)置,不過有個(gè)小坑要注意一下 旋轉(zhuǎn)動(dòng)畫一定要在平移動(dòng)畫前面,否則就不是滾動(dòng)平移了,而是亂跳。。。
這里貼一下動(dòng)畫的代碼設(shè)置方法
//顯示減號(hào)的動(dòng)畫
private Animation getShowAnimation(){
AnimationSet set = new AnimationSet(true);
RotateAnimation rotate = new RotateAnimation(0,720,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f);
set.addAnimation(rotate);
TranslateAnimation translate = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_SELF,2f
,TranslateAnimation.RELATIVE_TO_SELF,0
,TranslateAnimation.RELATIVE_TO_SELF,0
,TranslateAnimation.RELATIVE_TO_SELF,0);
set.addAnimation(translate);
AlphaAnimation alpha = new AlphaAnimation(0,1);
set.addAnimation(alpha);
set.setDuration(500);
return set;
}
//隱藏減號(hào)的動(dòng)畫
private Animation getHiddenAnimation(){
AnimationSet set = new AnimationSet(true);
RotateAnimation rotate = new RotateAnimation(0,720,RotateAnimation.RELATIVE_TO_SELF,0.5f,RotateAnimation.RELATIVE_TO_SELF,0.5f);
set.addAnimation(rotate);
TranslateAnimation translate = new TranslateAnimation(
TranslateAnimation.RELATIVE_TO_SELF,0
,TranslateAnimation.RELATIVE_TO_SELF,2f
,TranslateAnimation.RELATIVE_TO_SELF,0
,TranslateAnimation.RELATIVE_TO_SELF,0);
set.addAnimation(translate);
AlphaAnimation alpha = new AlphaAnimation(1,0);
set.addAnimation(alpha);
set.setDuration(500);
return set;
}
//執(zhí)行動(dòng)畫 只需給對(duì)應(yīng)控件setAnimation然后調(diào)用setVisibility方法即可
{
....
tvMinus.setAnimation(getHiddenAnimation());
tvMinus.setVisibility(View.GONE);
}
拋物線動(dòng)畫和上面的差不多可以分解成兩個(gè)平移動(dòng)畫,不過兩個(gè)平移動(dòng)畫的差值器一個(gè)線性一個(gè)加速而已,因?yàn)閯?dòng)畫界面跨度比較大所以需要在根部局內(nèi)寫,不能寫在列表的item中(這樣會(huì)顯示不全)。
代碼中的anim_mask_layout 即為整個(gè)布局文件的根布局,這里是一個(gè)RelativeLayout
實(shí)現(xiàn)過程:
1、首先點(diǎn)擊加號(hào)圖標(biāo),拿到控件在屏幕上的絕對(duì)坐標(biāo),回調(diào)activity顯示動(dòng)畫
int[] loc = new int[2]; v.getLocationInWindow(loc); activity.playAnimation(loc);
2、創(chuàng)建動(dòng)畫的控件并添加到根部局并在動(dòng)畫結(jié)束后移除動(dòng)畫view
public void playAnimation(int[] start_location){
ImageView img = new ImageView(this);
img.setImageResource(R.drawable.button_add);
setAnim(img,start_location);
}
//創(chuàng)建動(dòng)畫 平移動(dòng)畫直接傳遞偏移量
private Animation createAnim(int startX,int startY){
int[] des = new int[2];
imgCart.getLocationInWindow(des);
AnimationSet set = new AnimationSet(false);
Animation translationX = new TranslateAnimation(0, des[0]-startX, 0, 0);
//線性插值器 默認(rèn)就是線性
translationX.setInterpolator(new LinearInterpolator());
Animation translationY = new TranslateAnimation(0, 0, 0, des[1]-startY);
//設(shè)置加速插值器
translationY.setInterpolator(new AccelerateInterpolator());
Animation alpha = new AlphaAnimation(1,0.5f);
set.addAnimation(translationX);
set.addAnimation(translationY);
set.addAnimation(alpha);
set.setDuration(500);
return set;
}
//計(jì)算動(dòng)畫view在根部局中的坐標(biāo) 添加到根部局中
private void addViewToAnimLayout(final ViewGroup vg, final View view,
int[] location) {
int x = location[0];
int y = location[1];
int[] loc = new int[2];
vg.getLocationInWindow(loc);
view.setX(x);
view.setY(y-loc[1]);
vg.addView(view);
}
//設(shè)置動(dòng)畫結(jié)束移除動(dòng)畫view
private void setAnim(final View v, int[] start_location) {
addViewToAnimLayout(anim_mask_layout, v, start_location);
Animation set = createAnim(start_location[0],start_location[1]);
set.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(final Animation animation) {
//直接remove可能會(huì)因?yàn)榻缑嫒栽诶L制中成而報(bào)錯(cuò)
mHanlder.postDelayed(new Runnable() {
@Override
public void run() {
anim_mask_layout.removeView(v);
}
},100);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
v.startAnimation(set);
}
4:底部彈出購(gòu)物車清單
底部彈出的效果大家一定都很熟悉了,幾回每個(gè)項(xiàng)目中都會(huì)用的到,官方?jīng)]有提供簡(jiǎn)單的控件實(shí)現(xiàn),一般都需要自己寫,不過要做到簡(jiǎn)單流暢,便于移植推薦使用第三方庫(kù),這里向大家推薦一個(gè)
集成簡(jiǎn)單,效果多樣這里簡(jiǎn)單介紹一下使用方法
集成
compile 'com.flipboard:bottomsheet-core:1.5.1'
使用
xml中使用BottomSheetLayout包裹彈出view時(shí)候的背景布局,BottomSheetLayout繼承自幀布局:
<com.flipboard.bottomsheet.BottomSheetLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/bottomSheetLayout" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:layout_width="100dp" android:id="@+id/typeRecyclerView" android:layout_height="match_parent"> </android.support.v7.widget.RecyclerView> <se.emilsjolander.stickylistheaders.StickyListHeadersListView android:layout_width="match_parent" android:background="#fff" android:id="@+id/itemListView" android:layout_height="match_parent"> </se.emilsjolander.stickylistheaders.StickyListHeadersListView> </LinearLayout> </com.flipboard.bottomsheet.BottomSheetLayout>
代碼中使用很簡(jiǎn)單
//彈出View bottomSheet即是要彈出的view bottomSheetLayout.showWithSheetView(bottomSheet); //代碼隱藏view (點(diǎn)擊彈出view以外的地方可以隱藏彈出的view,向下滑動(dòng)也可以) bottomSheetLayout.dismissSheet();
5:數(shù)據(jù)的同步
同步數(shù)據(jù),控制界面刷新應(yīng)該是新手最容易繞彎的地方了,其實(shí)只要仔細(xì)一點(diǎn)也不難,這里簡(jiǎn)單提供一種思路(并不一定適合你的項(xiàng)目).
//商品列表 private ArrayList<GoodsItem> dataList; //分類列表 private ArrayList<GoodsItem> typeList; //已選擇的商品 private SparseArray<GoodsItem> selectedList; //用于記錄每個(gè)分組選擇的數(shù)目 private SparseIntArray groupSelect;
SparseArray這個(gè)類其實(shí)就是 HashMap< Integer,Object >
不過SparseArray既可以根據(jù)key查找Value,也可以根據(jù)位置查找value,性能比HashMap高,是官方推薦的替代類,
同樣SparseIntArray 其實(shí)是HashMap< Integer,Integer> 的替代者。
Activity里實(shí)現(xiàn)了下面幾個(gè)方法,用于數(shù)據(jù)統(tǒng)一管理
列表中顯示的商品購(gòu)買數(shù)量統(tǒng)一從activity獲取,商品的加減統(tǒng)一調(diào)用Activity的方法然后notifiDatasetChanged,由于代碼不少具體的還是看源碼吧
/**
* Item代表商品的購(gòu)買數(shù)量加一
* @param item
* @param refreshGoodList 是否刷新商品list
*/
public void add(GoodsItem item,boolean refreshGoodList){
int groupCount = groupSelect.get(item.typeId);
if(groupCount==0){
groupSelect.append(item.typeId,1);
}else{
groupSelect.append(item.typeId,++groupCount);
}
GoodsItem temp = selectedList.get(item.id);
if(temp==null){
item.count=1;
selectedList.append(item.id,item);
}else{
temp.count++;
}
update(refreshGoodList);
}
/**
* Item商品的購(gòu)買數(shù)量減一
* @param item
* @param refreshGoodList 是否刷新商品list
*/
public void remove(GoodsItem item,boolean refreshGoodList){
int groupCount = groupSelect.get(item.typeId);
if(groupCount==1){
groupSelect.delete(item.typeId);
}else if(groupCount>1){
groupSelect.append(item.typeId,--groupCount);
}
GoodsItem temp = selectedList.get(item.id);
if(temp!=null){
if(temp.count<2){
selectedList.remove(item.id);
}else{
item.count--;
}
}
update(refreshGoodList);
}
/**
* 刷新界面 總價(jià)、購(gòu)買數(shù)量等
* @param refreshGoodList 是否刷新商品list
*/
private void update(boolean refreshGoodList){
...
}
//根據(jù)商品id獲取當(dāng)前商品的采購(gòu)數(shù)量
public int getSelectedItemCountById(int id){
GoodsItem temp = selectedList.get(id);
if(temp==null){
return 0;
}
return temp.count;
}
//根據(jù)類別Id獲取屬于當(dāng)前類別的數(shù)量
public int getSelectedGroupCountByTypeId(int typeId){
return groupSelect.get(typeId);
}
具體邏輯還是看代碼吧,也許有更簡(jiǎn)單的實(shí)現(xiàn)。。。
Demo下載地址,下載到的文件是個(gè)AS module,你可以在自己新建的工程中Import Module.
源碼下載:Android仿外賣購(gòu)物車功能
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android實(shí)現(xiàn)購(gòu)物車功能
- Android實(shí)現(xiàn)的仿淘寶購(gòu)物車demo示例
- Android實(shí)現(xiàn)仿淘寶購(gòu)物車增加和減少商品數(shù)量功能demo示例
- Android Studio實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車功能
- Android實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車功能
- Android把商品添加到購(gòu)物車的動(dòng)畫效果(貝塞爾曲線)
- Android實(shí)現(xiàn)商城購(gòu)物車功能的實(shí)例代碼
- Android中實(shí)現(xiàn)淘寶購(gòu)物車RecyclerView或LIstView的嵌套選擇的邏輯
- Android實(shí)現(xiàn)添加商品到購(gòu)物車動(dòng)畫效果
- Android實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車
相關(guān)文章
Android自定義View實(shí)現(xiàn)多圖片選擇控件
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)多圖片選擇控件,具有一定的實(shí)用性,感興趣的小伙伴們可以參考一下2016-08-08
Android viewpager 3D畫廊的實(shí)現(xiàn)方法
ViewPager在開發(fā)中的使用頻率非常的高,接下來通過本文給大家分享android viewpager 3D畫廊的實(shí)現(xiàn)方法,需要的朋友參考下吧2017-02-02
Android 使用registerReceiver注冊(cè)BroadcastReceiver案例詳解
這篇文章主要介紹了Android 使用registerReceiver注冊(cè)BroadcastReceiver案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
Android Activity打開后被應(yīng)用快照遮住的問題
這篇文章主要介紹了Android Activity打開后被應(yīng)用快照遮住的問題,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01
音量控制鍵控制的音頻流(setVolumeControlStream)描述
當(dāng)開發(fā)多媒體應(yīng)用或者游戲應(yīng)用的時(shí)候,需要使用音量控制鍵來設(shè)置程序的音量大小,在Android系統(tǒng)中有多種音頻流,感興趣的朋友可以了解下2013-01-01
android圖庫(kù)豎屏不顯示status bar的解決方法
圖庫(kù)在JB和JB2的版本上顯示的行為是:橫屏全屏顯示,豎屏?xí)@示status bar,圖庫(kù)在JB和JB2的版本上顯示的行為是:橫屏全屏顯示,豎屏?xí)@示status bar,具體實(shí)現(xiàn)方法如下,不會(huì)的朋友可以參考下哈2013-06-06
Android ListView列表實(shí)現(xiàn)倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了Android ListView列表實(shí)現(xiàn)倒計(jì)時(shí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-09-09

