From 01c0d22a9149815df40757f118596a331cc26bd5 Mon Sep 17 00:00:00 2001 From: Peter Zhou Date: Thu, 8 Aug 2013 19:12:09 +0800 Subject: [PATCH] add KKExpendableListView and KKExpandableListViewFragment --- res/layout/fragment_expandable_listview.xml | 43 +++ res/layout/fragment_tabs.xml | 15 +- .../internal/image/KKImageRequest.java | 2 - .../internal/ui/KKListFragmentDelegate.java | 53 ++++ .../internal/ui/KKListViewDelegate.java | 254 +++++++++++++++++ .../ui/KKListViewOnLoadMoreListener.java | 19 ++ .../ui/KKListViewOnRefreshListener.java | 19 ++ .../toolkit/ui/KKExpandableListFragment.java | 51 ++++ .../toolkit/ui/KKExpandableListView.java | 83 ++++++ src/com/kkbox/toolkit/ui/KKListFragment.java | 20 +- src/com/kkbox/toolkit/ui/KKListView.java | 256 ++---------------- .../kkbox/toolkit/ui/KKSearchViewCompat.java | 1 - src/com/kkbox/toolkit/ui/ResizableButton.java | 14 + src/com/kkbox/toolkit/ui/ResizableView.java | 14 + src/com/kkbox/toolkit/utils/UserTask.java | 3 - 15 files changed, 595 insertions(+), 252 deletions(-) create mode 100644 res/layout/fragment_expandable_listview.xml create mode 100644 src/com/kkbox/toolkit/internal/ui/KKListFragmentDelegate.java create mode 100644 src/com/kkbox/toolkit/internal/ui/KKListViewDelegate.java create mode 100644 src/com/kkbox/toolkit/internal/ui/KKListViewOnLoadMoreListener.java create mode 100644 src/com/kkbox/toolkit/internal/ui/KKListViewOnRefreshListener.java create mode 100644 src/com/kkbox/toolkit/ui/KKExpandableListFragment.java create mode 100644 src/com/kkbox/toolkit/ui/KKExpandableListView.java diff --git a/res/layout/fragment_expandable_listview.xml b/res/layout/fragment_expandable_listview.xml new file mode 100644 index 0000000..326f80c --- /dev/null +++ b/res/layout/fragment_expandable_listview.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/fragment_tabs.xml b/res/layout/fragment_tabs.xml index 9a1e583..ec4621e 100644 --- a/res/layout/fragment_tabs.xml +++ b/res/layout/fragment_tabs.xml @@ -30,8 +30,8 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" - android:padding="8dp" - android:orientation="horizontal" > + android:orientation="horizontal" + android:padding="8dp" > @@ -41,11 +41,12 @@ android:layout_height="fill_parent" android:layout_below="@id/layout_radio_bar" android:orientation="vertical" > - + + 0) { + scrollIndex = listView.getFirstVisiblePosition(); + scrollPositionToTop = listView.getChildAt(0).getTop(); + } + } + +} \ No newline at end of file diff --git a/src/com/kkbox/toolkit/internal/ui/KKListViewDelegate.java b/src/com/kkbox/toolkit/internal/ui/KKListViewDelegate.java new file mode 100644 index 0000000..356114e --- /dev/null +++ b/src/com/kkbox/toolkit/internal/ui/KKListViewDelegate.java @@ -0,0 +1,254 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kkbox.toolkit.internal.ui; + +import android.content.Context; +import android.os.Handler; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.animation.LinearInterpolator; +import android.view.animation.RotateAnimation; +import android.widget.AbsListView; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; +import android.widget.ListView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.kkbox.toolkit.R; +import com.kkbox.toolkit.utils.StringUtils; + +public class KKListViewDelegate { + private static class State { + public static final int NORMAL = 0; + public static final int PULLING = 1; + public static final int PULLING_DOWN = 2; + public static final int UPDATING = 3; + public static final int LOADING_MORE = 4; + } + + private final ListView.OnScrollListener onScrollListener = new ListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) {} + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + headerViewIsFirstItem = (firstVisibleItem == 0) ? true : false; + boolean footerViewIsLastItem = false; + if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount != 0) { + footerViewIsLastItem = true; + } + if (footerViewIsLastItem && currentState == State.NORMAL && footerViewAdded) { + updateState(State.LOADING_MORE); + } + } + }; + + private ProgressBar progressPullToRefresh; + private View refreshHeaderView; + private View footerView; + private boolean footerViewAdded = false; + private TextView labelPullToRefresh; + private TextView labelPullToRefreshUpdatedAt; + private ImageView viewPullToRefresh; + private RotateAnimation animationFlip; + private RotateAnimation animationReverseFlip; + private KKListViewOnRefreshListener onRefreshListener; + private KKListViewOnLoadMoreListener onLoadMoreListener; + private int refreshHeaderhViewOriginHeight = 0; + private int firstItemToTopHeight = 0; + private float actionDownY = 0; + private int currentState = State.NORMAL; + private boolean headerViewIsFirstItem = false; + private Context context; + private ListView listView; + + public KKListViewDelegate(Context context, ListView listView) { + this.context = context; + this.listView = listView; + } + + public void setPullToRefresh(KKListViewOnRefreshListener onRefreshListener) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View headerView = inflater.inflate(R.layout.listview_header_pull_to_refresh, null); + refreshHeaderView = (RelativeLayout)headerView.findViewById(R.id.layout_pull_to_refresh_header); + labelPullToRefresh = (TextView)refreshHeaderView.findViewById(R.id.label_pull_to_refresh); + labelPullToRefreshUpdatedAt = (TextView)refreshHeaderView.findViewById(R.id.label_pull_to_refresh_updated_at); + viewPullToRefresh = (ImageView)refreshHeaderView.findViewById(R.id.view_pull_to_refresh); + progressPullToRefresh = (ProgressBar)refreshHeaderView.findViewById(R.id.progress_pull_to_refresh); + animationFlip = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); + animationFlip.setInterpolator(new LinearInterpolator()); + animationFlip.setDuration(250); + animationFlip.setFillAfter(true); + animationReverseFlip = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); + animationReverseFlip.setInterpolator(new LinearInterpolator()); + animationReverseFlip.setDuration(250); + animationReverseFlip.setFillAfter(true); + refreshHeaderhViewOriginHeight = (int)(60 * context.getResources().getDisplayMetrics().density); + listView.addHeaderView(headerView, null, false); + String lastUpdatedTime = (String)context.getResources().getText(R.string.last_update) + " " + + StringUtils.timeMillisToString(System.currentTimeMillis(), "yyyy-MM-dd HH:mm"); + labelPullToRefreshUpdatedAt.setText(lastUpdatedTime); + listView.setOnScrollListener(onScrollListener); + this.onRefreshListener = onRefreshListener; + } + + public void setLoadMore(KKListViewOnLoadMoreListener onLoadMoreListener) { + if (!footerViewAdded) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + footerView = inflater.inflate(R.layout.listview_item_load_more, null); + listView.addFooterView(footerView); + footerViewAdded = true; + } + this.onLoadMoreListener = onLoadMoreListener; + } + + private void updateState(int newState) { + if (newState != currentState) { + currentState = newState; + switch (currentState) { + case State.NORMAL: + resetHeaderViewHeight(0); + break; + case State.PULLING: + labelPullToRefresh.setText(R.string.pull_down_to_resort); + progressPullToRefresh.setVisibility(View.GONE); + viewPullToRefresh.setVisibility(View.VISIBLE); + viewPullToRefresh.clearAnimation(); + viewPullToRefresh.startAnimation(animationReverseFlip); + break; + case State.PULLING_DOWN: + labelPullToRefresh.setText(R.string.release_in_order_to_resort); + viewPullToRefresh.clearAnimation(); + viewPullToRefresh.startAnimation(animationFlip); + break; + case State.UPDATING: + resetHeaderViewHeight(refreshHeaderhViewOriginHeight); + viewPullToRefresh.clearAnimation(); + viewPullToRefresh.setVisibility(View.GONE); + progressPullToRefresh.setVisibility(View.VISIBLE); + labelPullToRefresh.setText(R.string.updating); + onRefreshListener.onRefresh(); + break; + case State.LOADING_MORE: + onLoadMoreListener.onLoadMore(); + break; + } + } + } + + public void onTouchEvent(MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_UP: + if (currentState == State.PULLING) { + resetHeaderViewHeight(0); + } + if (currentState == State.PULLING_DOWN) { + updateState(State.UPDATING); + } + break; + case MotionEvent.ACTION_DOWN: + actionDownY = event.getY(); + break; + case MotionEvent.ACTION_MOVE: + if (currentState == State.NORMAL) { + if (headerViewIsFirstItem && event.getY() - actionDownY > 10) { + updateState(State.PULLING); + } + } + if (currentState == State.PULLING) { + setHeaderViewHeight((int)(event.getY() - actionDownY)); + if (firstItemToTopHeight <= 0) { + updateState(State.NORMAL); + } else if (firstItemToTopHeight > refreshHeaderhViewOriginHeight) { + updateState(State.PULLING_DOWN); + } + event.setAction(MotionEvent.ACTION_CANCEL); + } + if (currentState == State.PULLING_DOWN) { + setHeaderViewHeight((int)(event.getY() - actionDownY)); + if (firstItemToTopHeight < refreshHeaderhViewOriginHeight) { + updateState(State.PULLING); + } + event.setAction(MotionEvent.ACTION_CANCEL); + } + } + } + + public void setAdapter() { + loadCompleted(); + } + + private void setHeaderViewHeight(int height) { + firstItemToTopHeight = height; + LinearLayout.LayoutParams containerLayoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); + if (firstItemToTopHeight <= 0) { + firstItemToTopHeight = 0; + containerLayoutParams.topMargin = refreshHeaderhViewOriginHeight * -1; + containerLayoutParams.height = refreshHeaderhViewOriginHeight; + } else if (firstItemToTopHeight <= refreshHeaderhViewOriginHeight) { + containerLayoutParams.topMargin = firstItemToTopHeight - refreshHeaderhViewOriginHeight; + containerLayoutParams.height = refreshHeaderhViewOriginHeight; + } else { + containerLayoutParams.topMargin = 0; + containerLayoutParams.height = firstItemToTopHeight; + } + refreshHeaderView.setLayoutParams(containerLayoutParams); + } + + private void resetHeaderViewHeight(final int targetHeight) { + if (firstItemToTopHeight != targetHeight) { + final int displacement = firstItemToTopHeight / 10; + final Handler handler = new Handler(); + Runnable runnable = new Runnable() { + public void run() { + if (firstItemToTopHeight > targetHeight) { + setHeaderViewHeight(firstItemToTopHeight - displacement); + handler.post(this); + } else { + firstItemToTopHeight = targetHeight; + setHeaderViewHeight(targetHeight); + handler.removeCallbacks(this); + } + } + }; + handler.post(runnable); + } + } + + public void loadCompleted() { + if (currentState == State.UPDATING) { + if (labelPullToRefreshUpdatedAt != null) { + String lastUpdatedTime = (String)context.getResources().getText(R.string.last_update) + " " + + StringUtils.timeMillisToString(System.currentTimeMillis(), "yyyy-MM-dd HH:mm"); + labelPullToRefreshUpdatedAt.setText(lastUpdatedTime); + } + if (!footerViewAdded && footerView != null) { + listView.addFooterView(footerView); + footerViewAdded = true; + } + } + updateState(State.NORMAL); + } + + public void loadMoreFinished() { + listView.removeFooterView(footerView); + footerViewAdded = false; + } +} diff --git a/src/com/kkbox/toolkit/internal/ui/KKListViewOnLoadMoreListener.java b/src/com/kkbox/toolkit/internal/ui/KKListViewOnLoadMoreListener.java new file mode 100644 index 0000000..82fcb1e --- /dev/null +++ b/src/com/kkbox/toolkit/internal/ui/KKListViewOnLoadMoreListener.java @@ -0,0 +1,19 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kkbox.toolkit.internal.ui; + +public abstract interface KKListViewOnLoadMoreListener { + public abstract void onLoadMore(); +} diff --git a/src/com/kkbox/toolkit/internal/ui/KKListViewOnRefreshListener.java b/src/com/kkbox/toolkit/internal/ui/KKListViewOnRefreshListener.java new file mode 100644 index 0000000..f17a66c --- /dev/null +++ b/src/com/kkbox/toolkit/internal/ui/KKListViewOnRefreshListener.java @@ -0,0 +1,19 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kkbox.toolkit.internal.ui; + +public abstract interface KKListViewOnRefreshListener { + public abstract void onRefresh(); +} diff --git a/src/com/kkbox/toolkit/ui/KKExpandableListFragment.java b/src/com/kkbox/toolkit/ui/KKExpandableListFragment.java new file mode 100644 index 0000000..941f007 --- /dev/null +++ b/src/com/kkbox/toolkit/ui/KKExpandableListFragment.java @@ -0,0 +1,51 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kkbox.toolkit.ui; + +import android.view.View; + +import com.kkbox.toolkit.internal.ui.KKListFragmentDelegate; + +public class KKExpandableListFragment extends KKFragment { + private KKListFragmentDelegate delegate = new KKListFragmentDelegate(); + + public KKExpandableListFragment() {} + + public KKExpandableListView getKKExpandableListView() { + return (KKExpandableListView)delegate.getListView(); + } + + @Override + protected void onLoadUI() { + super.onLoadUI(); + delegate.onLoadUI(); + } + + @Override + protected void initView(View view) { + super.initView(view); + delegate.initView(view); + } + + @Override + public void onPause() { + super.onPause(); + delegate.onPause(); + } + + public void saveListViewPosition() { + delegate.saveListViewPosition(); + } +} \ No newline at end of file diff --git a/src/com/kkbox/toolkit/ui/KKExpandableListView.java b/src/com/kkbox/toolkit/ui/KKExpandableListView.java new file mode 100644 index 0000000..aad11d1 --- /dev/null +++ b/src/com/kkbox/toolkit/ui/KKExpandableListView.java @@ -0,0 +1,83 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.kkbox.toolkit.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.ExpandableListAdapter; +import android.widget.ExpandableListView; + +import com.kkbox.toolkit.internal.ui.KKListViewDelegate; +import com.kkbox.toolkit.internal.ui.KKListViewOnLoadMoreListener; +import com.kkbox.toolkit.internal.ui.KKListViewOnRefreshListener; + +public class KKExpandableListView extends ExpandableListView { + + public static abstract class OnRefreshListener implements KKListViewOnRefreshListener { + public abstract void onRefresh(); + } + + public static abstract class OnLoadMoreListener implements KKListViewOnLoadMoreListener { + public abstract void onLoadMore(); + } + + private KKListViewDelegate delegate; + + public KKExpandableListView(Context context) { + this(context, null); + } + + public KKExpandableListView(Context context, AttributeSet attrs) { + this(context, attrs, -1); + } + + public KKExpandableListView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + delegate = new KKListViewDelegate(context, this); + } + + public void setPullToRefresh(OnRefreshListener onRefreshListener) { + delegate.setPullToRefresh(onRefreshListener); + } + + public void setLoadMore(OnLoadMoreListener onLoadMoreListener) { + delegate.setLoadMore(onLoadMoreListener); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + delegate.onTouchEvent(event); + try { + return super.onTouchEvent(event); + } catch (ArrayIndexOutOfBoundsException e) { + return false; + } + } + + @Override + public void setAdapter(ExpandableListAdapter adapter) { + super.setAdapter(adapter); + delegate.setAdapter(); + } + + public void loadCompleted() { + delegate.loadCompleted(); + } + + public void loadMoreFinished() { + delegate.loadMoreFinished(); + } +} diff --git a/src/com/kkbox/toolkit/ui/KKListFragment.java b/src/com/kkbox/toolkit/ui/KKListFragment.java index d16b176..8e45481 100644 --- a/src/com/kkbox/toolkit/ui/KKListFragment.java +++ b/src/com/kkbox/toolkit/ui/KKListFragment.java @@ -19,42 +19,36 @@ import android.view.View; -import com.kkbox.toolkit.R; +import com.kkbox.toolkit.internal.ui.KKListFragmentDelegate; public abstract class KKListFragment extends KKFragment { - private KKListView listView; - - private int scrollIndex = 0; - private int scrollPositionToTop = 0; + private KKListFragmentDelegate delegate = new KKListFragmentDelegate(); public KKListFragment() {} public KKListView getKKListView() { - return listView; + return (KKListView)delegate.getListView(); } @Override protected void onLoadUI() { super.onLoadUI(); - listView.setSelectionFromTop(scrollIndex, scrollPositionToTop); + delegate.onLoadUI(); } @Override protected void initView(View view) { super.initView(view); - listView = (KKListView)view.findViewById(R.id.listview); + delegate.initView(view); } @Override public void onPause() { super.onPause(); - saveListViewPosition(); + delegate.onPause(); } public void saveListViewPosition() { - if (listView.getChildCount() > 0) { - scrollIndex = listView.getFirstVisiblePosition(); - scrollPositionToTop = listView.getChildAt(0).getTop(); - } + delegate.saveListViewPosition(); } } \ No newline at end of file diff --git a/src/com/kkbox/toolkit/ui/KKListView.java b/src/com/kkbox/toolkit/ui/KKListView.java index ee663f1..043b10b 100644 --- a/src/com/kkbox/toolkit/ui/KKListView.java +++ b/src/com/kkbox/toolkit/ui/KKListView.java @@ -1,282 +1,86 @@ /* Copyright (C) 2013 KKBOX Inc. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* ​http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /** * KKListView */ package com.kkbox.toolkit.ui; import android.content.Context; -import android.os.Handler; import android.util.AttributeSet; -import android.util.Log; -import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.View; -import android.view.animation.LinearInterpolator; -import android.view.animation.RotateAnimation; -import android.widget.AbsListView; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.ListAdapter; import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.RelativeLayout; -import android.widget.TextView; -import com.kkbox.toolkit.R; -import com.kkbox.toolkit.utils.KKDebug; -import com.kkbox.toolkit.utils.StringUtils; +import com.kkbox.toolkit.internal.ui.KKListViewDelegate; +import com.kkbox.toolkit.internal.ui.KKListViewOnLoadMoreListener; +import com.kkbox.toolkit.internal.ui.KKListViewOnRefreshListener; public class KKListView extends ListView { - public static class State { - public static final int NORMAL = 0; - public static final int PULLING = 1; - public static final int PULLING_DOWN = 2; - public static final int UPDATING = 3; - public static final int LOADING_MORE = 4; - } - - public static abstract class OnRefreshListener { + public static abstract class OnRefreshListener implements KKListViewOnRefreshListener { public abstract void onRefresh(); } - public static abstract class OnLoadMoreListener { + public static abstract class OnLoadMoreListener implements KKListViewOnLoadMoreListener { public abstract void onLoadMore(); } - private final ListView.OnScrollListener onScrollListener = new ListView.OnScrollListener() { - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) {} - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - headerViewIsFirstItem = (firstVisibleItem == 0) ? true : false; - boolean footerViewIsLastItem = false; - if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount != 0) { - footerViewIsLastItem = true; - } - if (footerViewIsLastItem && currentState == State.NORMAL && footerViewAdded) { - updateState(State.LOADING_MORE); - } - } - }; - - private ProgressBar progressPullToRefresh; - private View refreshHeaderView; - private View footerView; - private boolean footerViewAdded = false; - private TextView labelPullToRefresh; - private TextView labelPullToRefreshUpdatedAt; - private ImageView viewPullToRefresh; - private RotateAnimation animationFlip; - private RotateAnimation animationReverseFlip; - private OnRefreshListener onRefreshListener; - private OnLoadMoreListener onLoadMoreListener; - private int refreshHeaderhViewOriginHeight = 0; - private int firstItemToTopHeight = 0; - private float actionDownY = 0; - private int currentState = State.NORMAL; - private boolean headerViewIsFirstItem = false; + private KKListViewDelegate delegate; public KKListView(Context context) { - super(context); + this(context, null); } public KKListView(Context context, AttributeSet attrs) { - super(context, attrs); + this(context, attrs, -1); } public KKListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + delegate = new KKListViewDelegate(context, this); } public void setPullToRefresh(OnRefreshListener onRefreshListener) { - LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - View headerView = inflater.inflate(R.layout.listview_header_pull_to_refresh, null); - refreshHeaderView = (RelativeLayout)headerView.findViewById(R.id.layout_pull_to_refresh_header); - labelPullToRefresh = (TextView)refreshHeaderView.findViewById(R.id.label_pull_to_refresh); - labelPullToRefreshUpdatedAt = (TextView)refreshHeaderView.findViewById(R.id.label_pull_to_refresh_updated_at); - viewPullToRefresh = (ImageView)refreshHeaderView.findViewById(R.id.view_pull_to_refresh); - progressPullToRefresh = (ProgressBar)refreshHeaderView.findViewById(R.id.progress_pull_to_refresh); - animationFlip = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); - animationFlip.setInterpolator(new LinearInterpolator()); - animationFlip.setDuration(250); - animationFlip.setFillAfter(true); - animationReverseFlip = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f); - animationReverseFlip.setInterpolator(new LinearInterpolator()); - animationReverseFlip.setDuration(250); - animationReverseFlip.setFillAfter(true); - refreshHeaderhViewOriginHeight = (int)(60 * getContext().getResources().getDisplayMetrics().density); - addHeaderView(headerView, null, false); - String lastUpdatedTime = (String)this.getResources().getText(R.string.last_update) + " " - + StringUtils.timeMillisToString(System.currentTimeMillis(), "yyyy-MM-dd HH:mm"); - labelPullToRefreshUpdatedAt.setText(lastUpdatedTime); - setOnScrollListener(onScrollListener); - this.onRefreshListener = onRefreshListener; + delegate.setPullToRefresh(onRefreshListener); } public void setLoadMore(OnLoadMoreListener onLoadMoreListener) { - if (!footerViewAdded) { - LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); - footerView = inflater.inflate(R.layout.listview_item_load_more, null); - addFooterView(footerView); - footerViewAdded = true; - } - this.onLoadMoreListener = onLoadMoreListener; + delegate.setLoadMore(onLoadMoreListener); } - - private void updateState(int newState) { - if (newState != currentState) { - currentState = newState; - switch (currentState) { - case State.NORMAL: - resetHeaderViewHeight(0); - break; - case State.PULLING: - labelPullToRefresh.setText(R.string.pull_down_to_resort); - progressPullToRefresh.setVisibility(View.GONE); - viewPullToRefresh.setVisibility(View.VISIBLE); - viewPullToRefresh.clearAnimation(); - viewPullToRefresh.startAnimation(animationReverseFlip); - break; - case State.PULLING_DOWN: - labelPullToRefresh.setText(R.string.release_in_order_to_resort); - viewPullToRefresh.clearAnimation(); - viewPullToRefresh.startAnimation(animationFlip); - break; - case State.UPDATING: - resetHeaderViewHeight(refreshHeaderhViewOriginHeight); - viewPullToRefresh.clearAnimation(); - viewPullToRefresh.setVisibility(View.GONE); - progressPullToRefresh.setVisibility(View.VISIBLE); - labelPullToRefresh.setText(R.string.updating); - onRefreshListener.onRefresh(); - break; - case State.LOADING_MORE: - onLoadMoreListener.onLoadMore(); - break; - } - } - } - + @Override public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_UP: - if (currentState == State.PULLING) { - resetHeaderViewHeight(0); - } - if (currentState == State.PULLING_DOWN) { - updateState(State.UPDATING); - } - break; - case MotionEvent.ACTION_DOWN: - actionDownY = event.getY(); - break; - case MotionEvent.ACTION_MOVE: - if (currentState == State.NORMAL) { - if (headerViewIsFirstItem && event.getY() - actionDownY > 10) { - updateState(State.PULLING); - } - } - if (currentState == State.PULLING) { - setHeaderViewHeight((int)(event.getY() - actionDownY)); - if (firstItemToTopHeight <= 0) { - updateState(State.NORMAL); - } else if (firstItemToTopHeight > refreshHeaderhViewOriginHeight) { - updateState(State.PULLING_DOWN); - } - event.setAction(MotionEvent.ACTION_CANCEL); - } - if (currentState == State.PULLING_DOWN) { - setHeaderViewHeight((int)(event.getY() - actionDownY)); - if (firstItemToTopHeight < refreshHeaderhViewOriginHeight) { - updateState(State.PULLING); - } - event.setAction(MotionEvent.ACTION_CANCEL); - } - } + delegate.onTouchEvent(event); try { return super.onTouchEvent(event); } catch (ArrayIndexOutOfBoundsException e) { - KKDebug.w(Log.getStackTraceString(e)); return false; } } - + @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); - loadCompleted(); - } - - private void setHeaderViewHeight(int height) { - firstItemToTopHeight = height; - LinearLayout.LayoutParams containerLayoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); - if (firstItemToTopHeight <= 0) { - firstItemToTopHeight = 0; - containerLayoutParams.topMargin = refreshHeaderhViewOriginHeight * -1; - containerLayoutParams.height = refreshHeaderhViewOriginHeight; - } else if (firstItemToTopHeight <= refreshHeaderhViewOriginHeight) { - containerLayoutParams.topMargin = firstItemToTopHeight - refreshHeaderhViewOriginHeight; - containerLayoutParams.height = refreshHeaderhViewOriginHeight; - } else { - containerLayoutParams.topMargin = 0; - containerLayoutParams.height = firstItemToTopHeight; - } - refreshHeaderView.setLayoutParams(containerLayoutParams); - } - - private void resetHeaderViewHeight(final int targetHeight) { - if (firstItemToTopHeight != targetHeight) { - final int displacement = firstItemToTopHeight / 10; - final Handler handler = new Handler(); - Runnable runnable = new Runnable() { - public void run() { - if (firstItemToTopHeight > targetHeight) { - setHeaderViewHeight(firstItemToTopHeight - displacement); - handler.post(this); - } else { - firstItemToTopHeight = targetHeight; - setHeaderViewHeight(targetHeight); - handler.removeCallbacks(this); - } - } - }; - handler.post(runnable); - } + delegate.setAdapter(); } public void loadCompleted() { - if (currentState == State.UPDATING) { - if (labelPullToRefreshUpdatedAt != null) { - String lastUpdatedTime = (String)this.getResources().getText(R.string.last_update) + " " - + StringUtils.timeMillisToString(System.currentTimeMillis(), "yyyy-MM-dd HH:mm"); - labelPullToRefreshUpdatedAt.setText(lastUpdatedTime); - } - if (!footerViewAdded && footerView != null) { - addFooterView(footerView); - footerViewAdded = true; - } - } - updateState(State.NORMAL); + delegate.loadCompleted(); } public void loadMoreFinished() { - removeFooterView(footerView); - footerViewAdded = false; + delegate.loadMoreFinished(); } } diff --git a/src/com/kkbox/toolkit/ui/KKSearchViewCompat.java b/src/com/kkbox/toolkit/ui/KKSearchViewCompat.java index 2582615..e7736ea 100644 --- a/src/com/kkbox/toolkit/ui/KKSearchViewCompat.java +++ b/src/com/kkbox/toolkit/ui/KKSearchViewCompat.java @@ -18,7 +18,6 @@ package com.kkbox.toolkit.ui; import android.app.SearchManager; -import android.app.SearchableInfo; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; diff --git a/src/com/kkbox/toolkit/ui/ResizableButton.java b/src/com/kkbox/toolkit/ui/ResizableButton.java index 08ce349..a9302e5 100644 --- a/src/com/kkbox/toolkit/ui/ResizableButton.java +++ b/src/com/kkbox/toolkit/ui/ResizableButton.java @@ -1,3 +1,17 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.kkbox.toolkit.ui; import android.content.Context; diff --git a/src/com/kkbox/toolkit/ui/ResizableView.java b/src/com/kkbox/toolkit/ui/ResizableView.java index f136f8e..4ce3c45 100644 --- a/src/com/kkbox/toolkit/ui/ResizableView.java +++ b/src/com/kkbox/toolkit/ui/ResizableView.java @@ -1,3 +1,17 @@ +/* Copyright (C) 2013 KKBOX Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * ​http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.kkbox.toolkit.ui; import android.content.Context; diff --git a/src/com/kkbox/toolkit/utils/UserTask.java b/src/com/kkbox/toolkit/utils/UserTask.java index a27ba54..24d763f 100644 --- a/src/com/kkbox/toolkit/utils/UserTask.java +++ b/src/com/kkbox/toolkit/utils/UserTask.java @@ -1,6 +1,3 @@ -/** - * UserTask: This is a third-party component. - */ /* * Copyright (C) 2008 The Android Open Source Project, Romain Guy