mirror of
https://gitee.com/houstudio/Cdroid.git
synced 2024-12-04 05:10:06 +08:00
remove expandablelistview,use recyclerview PLS
This commit is contained in:
parent
98e2ac97a1
commit
2d42312380
@ -2,8 +2,23 @@
|
||||
namespace cdroid{
|
||||
|
||||
int Parcel::readInt(){
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
bool Parcel::readBoolean(){
|
||||
return readInt()!=0;
|
||||
}
|
||||
|
||||
std::string Parcel::readString(){
|
||||
return std::string();
|
||||
}
|
||||
|
||||
void Parcel::writeInt(int val){
|
||||
}
|
||||
|
||||
void Parcel::writeBoolean(bool){
|
||||
}
|
||||
|
||||
void Parcel::writeString(const std::string&){
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,12 +60,14 @@ public:
|
||||
void recycle();
|
||||
uint8_t readByte();
|
||||
int readInt();
|
||||
bool readBoolean();
|
||||
float readFloat();
|
||||
double readDouble();
|
||||
std::string readString();
|
||||
|
||||
void writeByte(uint8_t val);
|
||||
void writeInt(int32_t val);
|
||||
void writeBoolean(bool);
|
||||
void writeFloat(float val);
|
||||
void writeDouble(double val);
|
||||
void writeString(const std::string& val);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#ifndef __PARCELABLE_H__
|
||||
#define __PARCELABLE_H__
|
||||
class Parcel;
|
||||
namespace cdroid{
|
||||
class Parcelable {
|
||||
public:
|
||||
|
33
src/gui/view/abssavedstate.cc
Executable file
33
src/gui/view/abssavedstate.cc
Executable file
@ -0,0 +1,33 @@
|
||||
#include <view/abssavedstate.h>
|
||||
namespace cdroid{
|
||||
AbsSavedState AbsSavedState::EMPTY_STATE;
|
||||
|
||||
AbsSavedState::AbsSavedState() {
|
||||
mSuperState = nullptr;
|
||||
}
|
||||
|
||||
AbsSavedState::AbsSavedState(Parcelable* superState) {
|
||||
LOGE_IF(superState == nullptr,"superState must not be null");
|
||||
mSuperState = superState != &EMPTY_STATE ? superState : nullptr;
|
||||
}
|
||||
|
||||
AbsSavedState::AbsSavedState(Parcel& source) {
|
||||
//this(source, null);
|
||||
}
|
||||
|
||||
/*AbsSavedState(Parcel& source, ClassLoader loader) {
|
||||
Parcelable superState = source.readParcelable(loader);
|
||||
mSuperState = superState != null ? superState : EMPTY_STATE;
|
||||
}*/
|
||||
|
||||
Parcelable* AbsSavedState::getSuperState() {
|
||||
return mSuperState;
|
||||
}
|
||||
|
||||
int AbsSavedState::describeContents() {
|
||||
return 0;
|
||||
}
|
||||
void AbsSavedState::writeToParcel(Parcel& dest, int flags) {
|
||||
//dest.writeParcelable(mSuperState, flags);
|
||||
}
|
||||
}
|
21
src/gui/view/abssavedstate.h
Executable file
21
src/gui/view/abssavedstate.h
Executable file
@ -0,0 +1,21 @@
|
||||
#ifndef __ABS_SAVED_STATE_H__
|
||||
#define __ABS_SAVED_STATE_H__
|
||||
#include <core/parcelable.h>
|
||||
#include <core/parcel.h>
|
||||
namespace cdroid{
|
||||
class AbsSavedState:public Parcelable {
|
||||
public:
|
||||
static AbsSavedState EMPTY_STATE;
|
||||
private:
|
||||
Parcelable* mSuperState;
|
||||
AbsSavedState();
|
||||
protected:
|
||||
AbsSavedState(Parcelable* superState);
|
||||
AbsSavedState(Parcel& source);
|
||||
public:
|
||||
Parcelable* getSuperState();
|
||||
int describeContents();
|
||||
void writeToParcel(Parcel& dest, int flags);
|
||||
};
|
||||
}/*endof namespace*/
|
||||
#endif
|
@ -1448,7 +1448,7 @@ void View::saveHierarchyState(SparseArray<Parcelable*>& container){
|
||||
void View::dispatchSaveInstanceState(SparseArray<Parcelable*>& container){
|
||||
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
|
||||
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
|
||||
Parcelable* state = onSaveInstanceState();
|
||||
Parcelable* state = onSaveInstanceState();
|
||||
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
|
||||
LOGE("Derived class did not call super.onSaveInstanceState()");
|
||||
}
|
||||
@ -1461,29 +1461,30 @@ void View::dispatchSaveInstanceState(SparseArray<Parcelable*>& container){
|
||||
|
||||
Parcelable* View::onSaveInstanceState(){
|
||||
#if 0
|
||||
#define LAST_APP_AUTOFILL_ID 100000
|
||||
mPrivateFlags |= PFLAG_SAVE_STATE_CALLED;
|
||||
if (mStartActivityRequestWho != null || isAutofilled()
|
||||
if (mStartActivityRequestWho.size() || isAutofilled()
|
||||
|| mAutofillViewId > LAST_APP_AUTOFILL_ID) {
|
||||
BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE);
|
||||
BaseSavedState* state = new BaseSavedState(&AbsSavedState::EMPTY_STATE);
|
||||
|
||||
if (mStartActivityRequestWho != null) {
|
||||
state.mSavedData |= BaseSavedState.START_ACTIVITY_REQUESTED_WHO_SAVED;
|
||||
if (mStartActivityRequestWho.size()) {
|
||||
state->mSavedData |= BaseSavedState::START_ACTIVITY_REQUESTED_WHO_SAVED;
|
||||
}
|
||||
|
||||
if (isAutofilled()) {
|
||||
state.mSavedData |= BaseSavedState.IS_AUTOFILLED;
|
||||
state->mSavedData |= BaseSavedState::IS_AUTOFILLED;
|
||||
}
|
||||
|
||||
if (mAutofillViewId > LAST_APP_AUTOFILL_ID) {
|
||||
state.mSavedData |= BaseSavedState.AUTOFILL_ID;
|
||||
state->mSavedData |= BaseSavedState::AUTOFILL_ID;
|
||||
}
|
||||
|
||||
state.mStartActivityRequestWhoSaved = mStartActivityRequestWho;
|
||||
state.mIsAutofilled = isAutofilled();
|
||||
state.mAutofillViewId = mAutofillViewId;
|
||||
state->mStartActivityRequestWhoSaved = mStartActivityRequestWho;
|
||||
state->mIsAutofilled = isAutofilled();
|
||||
state->mAutofillViewId = mAutofillViewId;
|
||||
return state;
|
||||
}
|
||||
return BaseSavedState.EMPTY_STATE;
|
||||
return &BaseSavedState::EMPTY_STATE;
|
||||
#endif
|
||||
return nullptr;//Parcelable();
|
||||
}
|
||||
@ -1495,12 +1496,11 @@ void View::restoreHierarchyState(SparseArray<Parcelable*>& container){
|
||||
void View::dispatchRestoreInstanceState(SparseArray<Parcelable*>& container){
|
||||
if (mID != NO_ID) {
|
||||
#if 0
|
||||
auto it= container.find(mID);
|
||||
if (it!=container.end()){//state != null) {
|
||||
Parcelable state = it->second;//container.get(mID);
|
||||
LOGD("View %p:%d Restoreing #",this,mID,state.c_str());
|
||||
auto state= container.get(mID);
|
||||
if (state != nullptr) {
|
||||
LOGD("View %p:%d Restoreing #",this,mID,state);
|
||||
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
|
||||
onRestoreInstanceState(state);
|
||||
onRestoreInstanceState(*state);
|
||||
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
|
||||
FATAL("Derived class did not call super.onRestoreInstanceState()");
|
||||
}
|
||||
@ -6447,7 +6447,7 @@ bool View::postDelayed(const std::function<void()>&what,uint32_t delay){
|
||||
|
||||
bool View::removeCallbacks(const Runnable& what){
|
||||
View*root = getRootView();
|
||||
if(root&&(root!=this))
|
||||
if( root && (root!=this) )
|
||||
return root->removeCallbacks(what);
|
||||
return false;
|
||||
}
|
||||
@ -6476,7 +6476,7 @@ void View::requestLayout(){
|
||||
if (mParent != nullptr && !mParent->isLayoutRequested()) {
|
||||
mParent->requestLayout();
|
||||
}
|
||||
if (mAttachInfo && mAttachInfo->mViewRequestingLayout == this) {
|
||||
if ( mAttachInfo && (mAttachInfo->mViewRequestingLayout == this) ) {
|
||||
mAttachInfo->mViewRequestingLayout = nullptr;
|
||||
}
|
||||
}
|
||||
@ -7455,4 +7455,25 @@ const std::string View::MeasureSpec::toString(int measureSpec) {
|
||||
return sb.str();
|
||||
}
|
||||
|
||||
View::BaseSavedState::BaseSavedState(Parcel& source)
|
||||
:AbsSavedState(source){
|
||||
mSavedData = source.readInt();
|
||||
mStartActivityRequestWhoSaved = source.readString();
|
||||
mIsAutofilled = source.readBoolean();
|
||||
mHideHighlight = source.readBoolean();
|
||||
mAutofillViewId = source.readInt();
|
||||
}
|
||||
|
||||
View::BaseSavedState::BaseSavedState(Parcelable* superState)
|
||||
:AbsSavedState(superState){
|
||||
}
|
||||
|
||||
void View::BaseSavedState::writeToParcel(Parcel& out, int flags) {
|
||||
AbsSavedState::writeToParcel(out, flags);
|
||||
out.writeInt(mSavedData);
|
||||
out.writeString(mStartActivityRequestWhoSaved);
|
||||
out.writeBoolean(mIsAutofilled);
|
||||
out.writeBoolean(mHideHighlight);
|
||||
out.writeInt(mAutofillViewId);
|
||||
}
|
||||
}//endof namespace
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <core/parcel.h>
|
||||
#include <core/parcelable.h>
|
||||
#include <cairomm/pattern.h>
|
||||
#include <view/abssavedstate.h>
|
||||
#include <view/gravity.h>
|
||||
#include <view/layoutparams.h>
|
||||
#include <view/rendernode.h>
|
||||
@ -51,6 +52,7 @@ public:
|
||||
static bool VIEW_DEBUG;
|
||||
static int mViewCount;
|
||||
class MeasureSpec;
|
||||
class BaseSavedState;
|
||||
constexpr static int DEBUG_CORNERS_COLOR = 0xFF3f7fff;
|
||||
constexpr static int DEBUG_CORNERS_SIZE_DIP = 8;
|
||||
constexpr static int NO_ID =-1;
|
||||
@ -1313,6 +1315,24 @@ public:
|
||||
static const std::string toString(int measureSpec) ;
|
||||
};
|
||||
using MeasureSpec=View::MeasureSpec;
|
||||
|
||||
class View::BaseSavedState:public AbsSavedState {
|
||||
public:
|
||||
static constexpr int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1;
|
||||
static constexpr int IS_AUTOFILLED = 0b10;
|
||||
static constexpr int AUTOFILL_ID = 0b100;
|
||||
public:
|
||||
// Flags that describe what data in this state is valid
|
||||
int mSavedData;
|
||||
std::string mStartActivityRequestWhoSaved;
|
||||
bool mIsAutofilled;
|
||||
bool mHideHighlight;
|
||||
int mAutofillViewId;
|
||||
public:
|
||||
BaseSavedState(Parcel& source);
|
||||
BaseSavedState(Parcelable* superState);
|
||||
void writeToParcel(Parcel& out, int flags);
|
||||
};
|
||||
}//endof namespace cdroid
|
||||
|
||||
using namespace cdroid;
|
||||
|
@ -1,29 +0,0 @@
|
||||
#include <widget/baseexpandablelistadapter.h>
|
||||
namespace cdroid{
|
||||
|
||||
void BaseExpandableListAdapter::registerDataSetObserver(DataSetObserver* observer) {
|
||||
//mDataSetObservable->registerObserver(observer);
|
||||
auto it = std::find(mDataObservers.begin(),mDataObservers.end(),observer);
|
||||
if(it != mDataObservers.end())mDataObservers.push_back(observer);
|
||||
}
|
||||
|
||||
void BaseExpandableListAdapter::unregisterDataSetObserver(DataSetObserver* observer) {
|
||||
//mDataSetObservable->unregisterObserver(observer);
|
||||
auto it = std::find(mDataObservers.begin(),mDataObservers.end(),observer);
|
||||
if(it != mDataObservers.end())mDataObservers.erase(it);
|
||||
}
|
||||
|
||||
void BaseExpandableListAdapter::notifyDataSetInvalidated() {
|
||||
//mDataSetObservable->notifyInvalidated();
|
||||
for(auto it=mDataObservers.begin();it!=mDataObservers.end();it++)
|
||||
(*it)->onInvalidated();
|
||||
}
|
||||
|
||||
void BaseExpandableListAdapter::notifyDataSetChanged() {
|
||||
//mDataSetObservable.notifyChanged();
|
||||
for(auto it=mDataObservers.begin();it!=mDataObservers.end();it++)
|
||||
(*it)->onChanged();
|
||||
}
|
||||
|
||||
}/*endof namespace*/
|
||||
|
@ -1,60 +0,0 @@
|
||||
#ifndef __BASEEXPANDABLE_LISTADAPTER_H__
|
||||
#define __BASEEXPANDABLE_LISTADAPTER_H__
|
||||
#include <widget/adapter.h>
|
||||
#include <widget/expandablelistadapter.h>
|
||||
|
||||
namespace cdroid{
|
||||
|
||||
class BaseExpandableListAdapter:virtual public ExpandableListAdapter{//virtual public HeterogeneousExpandableList{
|
||||
private:
|
||||
//DataSetObservable* mDataSetObservable;
|
||||
std::vector<DataSetObserver*>mDataObservers;
|
||||
public:
|
||||
void registerDataSetObserver(DataSetObserver* observer)override;
|
||||
void unregisterDataSetObserver(DataSetObserver* observer)override;
|
||||
virtual void notifyDataSetInvalidated();
|
||||
virtual void notifyDataSetChanged();
|
||||
bool areAllItemsEnabled(){
|
||||
return true;
|
||||
}
|
||||
|
||||
void onGroupCollapsed(int groupPosition)override{
|
||||
}
|
||||
|
||||
void onGroupExpanded(int groupPosition)override{
|
||||
}
|
||||
|
||||
int64_t getCombinedChildId(long groupId, long childId)override{
|
||||
return 0x8000000000000000L | (int64_t((groupId & 0x7FFFFFFF)) << 32) | (childId & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
|
||||
int64_t getCombinedGroupId(long groupId)override{
|
||||
return int64_t((groupId & 0x7FFFFFFF)) << 32;
|
||||
}
|
||||
|
||||
bool isEmpty()override{
|
||||
return getGroupCount() == 0;
|
||||
}
|
||||
|
||||
|
||||
int getChildType(int groupPosition, int childPosition) override{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getChildTypeCount()override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int getGroupType(int groupPosition)override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getGroupTypeCount()override{
|
||||
return 1;
|
||||
}
|
||||
};
|
||||
|
||||
}/*endof namespace*/
|
||||
#endif/*__BASEEXPANDABLE_LISTADAPTER_H__*/
|
@ -1,239 +0,0 @@
|
||||
#include <widget/expandablelayout.h>
|
||||
#include <widget/linearlayout.h>
|
||||
|
||||
//Referenced from
|
||||
//https://github.com/cachapa/ExpandableLayout/blob/master/lib/src/main/java/net/cachapa/expandablelayout/ExpandableLayout.java
|
||||
|
||||
namespace cdroid{
|
||||
|
||||
ExpandableLayout::ExpandableLayout(int w,int h):FrameLayout(w,h){
|
||||
initView();
|
||||
}
|
||||
|
||||
ExpandableLayout::ExpandableLayout(Context* context, const AttributeSet& attrs)
|
||||
:FrameLayout(context, attrs){
|
||||
initView();
|
||||
mDuration = attrs.getInt("duration", DEFAULT_DURATION);
|
||||
mExpansion = attrs.getBoolean("expanded", false) ? 1 : 0;
|
||||
mOrientation = attrs.getInt("orientation", VERTICAL);
|
||||
mParallax = attrs.getFloat("parallax", 1);
|
||||
|
||||
mState = mExpansion == 0 ? COLLAPSED : EXPANDED;
|
||||
setParallax(mParallax);
|
||||
}
|
||||
|
||||
void ExpandableLayout::initView(){
|
||||
mDuration = DEFAULT_DURATION;
|
||||
mExpansion =0;
|
||||
mOrientation=VERTICAL;
|
||||
mParallax=1;
|
||||
mState =COLLAPSED;
|
||||
mAnimator = nullptr;
|
||||
mInterpolator = new FastOutSlowInInterpolator();
|
||||
}
|
||||
|
||||
ExpandableLayout::~ExpandableLayout(){
|
||||
delete mInterpolator;
|
||||
}
|
||||
|
||||
void ExpandableLayout::onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
FrameLayout::onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
const int width = getMeasuredWidth();
|
||||
const int height = getMeasuredHeight();
|
||||
|
||||
const int size = mOrientation == LinearLayout::HORIZONTAL ? width : height;
|
||||
|
||||
setVisibility(mExpansion == 0 && size == 0 ? GONE : VISIBLE);
|
||||
|
||||
int expansionDelta = size - std::round(size * mExpansion);
|
||||
if (mParallax > 0) {
|
||||
float parallaxDelta = expansionDelta * mParallax;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View* child = getChildAt(i);
|
||||
if (mOrientation == HORIZONTAL) {
|
||||
int direction = -1;
|
||||
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
|
||||
direction = 1;
|
||||
}
|
||||
child->setTranslationX(direction * parallaxDelta);
|
||||
} else {
|
||||
child->setTranslationY(-parallaxDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mOrientation == HORIZONTAL) {
|
||||
setMeasuredDimension(width - expansionDelta, height);
|
||||
} else {
|
||||
setMeasuredDimension(width, height - expansionDelta);
|
||||
}
|
||||
}
|
||||
|
||||
/*void ExpandableLayout::onConfigurationChanged(Configuration newConfig) {
|
||||
if (mAnimator != null) {
|
||||
mAnimator->cancel();
|
||||
}
|
||||
FrameLayout::onConfigurationChanged(newConfig);
|
||||
}*/
|
||||
|
||||
int ExpandableLayout::getState()const{
|
||||
return mState;
|
||||
}
|
||||
|
||||
bool ExpandableLayout::isExpanded()const{
|
||||
return mState == EXPANDING || mState == EXPANDED;
|
||||
}
|
||||
|
||||
void ExpandableLayout::toggle() {
|
||||
toggle(true);
|
||||
}
|
||||
|
||||
void ExpandableLayout::toggle(bool animate) {
|
||||
if (isExpanded()) {
|
||||
collapse(animate);
|
||||
} else {
|
||||
expand(animate);
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandableLayout::expand() {
|
||||
expand(true);
|
||||
}
|
||||
|
||||
void ExpandableLayout::expand(bool animate) {
|
||||
setExpanded(true, animate);
|
||||
}
|
||||
|
||||
void ExpandableLayout::collapse() {
|
||||
collapse(true);
|
||||
}
|
||||
|
||||
void ExpandableLayout::collapse(bool animate) {
|
||||
setExpanded(false, animate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method - same as calling setExpanded(expanded, true)
|
||||
*/
|
||||
void ExpandableLayout::setExpanded(bool expand) {
|
||||
setExpanded(expand, true);
|
||||
}
|
||||
|
||||
void ExpandableLayout::setExpanded(bool expand, bool animate) {
|
||||
if (expand == isExpanded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int targetExpansion = expand ? 1 : 0;
|
||||
if (animate) {
|
||||
animateSize(targetExpansion);
|
||||
} else {
|
||||
setExpansion(targetExpansion);
|
||||
}
|
||||
}
|
||||
|
||||
int ExpandableLayout::getDuration()const{
|
||||
return mDuration;
|
||||
}
|
||||
|
||||
void ExpandableLayout::setInterpolator(Interpolator* interpolator) {
|
||||
this->mInterpolator = interpolator;
|
||||
}
|
||||
|
||||
void ExpandableLayout::setDuration(int duration) {
|
||||
this->mDuration = duration;
|
||||
}
|
||||
|
||||
float ExpandableLayout::getExpansion()const{
|
||||
return mExpansion;
|
||||
}
|
||||
|
||||
void ExpandableLayout::setExpansion(float expansion) {
|
||||
if (this->mExpansion == expansion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Infer state from previous value
|
||||
float delta = expansion - this->mExpansion;
|
||||
if (expansion == 0) {
|
||||
mState = COLLAPSED;
|
||||
} else if (expansion == 1) {
|
||||
mState = EXPANDED;
|
||||
} else if (delta < 0) {
|
||||
mState = COLLAPSING;
|
||||
} else if (delta > 0) {
|
||||
mState = EXPANDING;
|
||||
}
|
||||
|
||||
setVisibility(mState == COLLAPSED ? GONE : VISIBLE);
|
||||
this->mExpansion = expansion;
|
||||
requestLayout();
|
||||
|
||||
if (mListener != nullptr) {
|
||||
mListener(expansion, mState);
|
||||
}
|
||||
}
|
||||
|
||||
float ExpandableLayout::getParallax()const{
|
||||
return mParallax;
|
||||
}
|
||||
|
||||
void ExpandableLayout::setParallax(float parallax) {
|
||||
// Make sure parallax is between 0 and 1
|
||||
parallax = std::min(1.f, std::max(0.f, parallax));
|
||||
this->mParallax = parallax;
|
||||
}
|
||||
|
||||
int ExpandableLayout::getOrientation()const{
|
||||
return mOrientation;
|
||||
}
|
||||
|
||||
void ExpandableLayout::setOrientation(int orientation) {
|
||||
if (orientation < 0 || orientation > 1) {
|
||||
FATAL("Orientation(%d) must be either 0 (horizontal) or 1 (vertical)",orientation);
|
||||
}
|
||||
this->mOrientation = orientation;
|
||||
}
|
||||
|
||||
void ExpandableLayout::setOnExpansionUpdateListener(OnExpansionUpdateListener listener) {
|
||||
this->mListener = listener;
|
||||
}
|
||||
|
||||
void ExpandableLayout::animateSize(int targetExpansion) {
|
||||
mTargetExpansion = targetExpansion;
|
||||
mCanceled = false;
|
||||
if (mAnimator != nullptr) {
|
||||
mAnimator->cancel();
|
||||
//delete mAnimator;
|
||||
//mAnimator = nullptr;
|
||||
mAnimator->setFloatValues({mExpansion, (float)targetExpansion});
|
||||
}else{
|
||||
mAnimator = ValueAnimator::ofFloat({mExpansion, (float)targetExpansion});
|
||||
mAnimator->setInterpolator(mInterpolator);
|
||||
mAnimator->setDuration(mDuration);
|
||||
|
||||
mAnimator->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim) {
|
||||
setExpansion(GET_VARIANT(anim.getAnimatedValue(),float));
|
||||
}));
|
||||
|
||||
Animator::AnimatorListener ls;
|
||||
ls.onAnimationStart=[this](Animator&anim,bool isReverse){
|
||||
mState = mTargetExpansion ==0 ? COLLAPSING : EXPANDING;
|
||||
};
|
||||
ls.onAnimationEnd=[this](Animator&anim,bool isReverse){
|
||||
if (!mCanceled) {
|
||||
mState = mTargetExpansion == 0 ? COLLAPSED : EXPANDED;
|
||||
setExpansion(mTargetExpansion);
|
||||
}
|
||||
};
|
||||
ls.onAnimationCancel=[this](Animator&anim){
|
||||
mCanceled = true;
|
||||
};
|
||||
mAnimator->addListener(ls);
|
||||
}
|
||||
mAnimator->start();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,61 +0,0 @@
|
||||
#ifndef __EXPANDABLE_LAYOUT_H__
|
||||
#define __EXPANDABLE_LAYOUT_H__
|
||||
#include <widget/framelayout.h>
|
||||
namespace cdroid{
|
||||
|
||||
class ExpandableLayout:public FrameLayout{
|
||||
private:
|
||||
static constexpr int DEFAULT_DURATION = 300;
|
||||
public:
|
||||
static constexpr int COLLAPSED = 0;
|
||||
static constexpr int COLLAPSING = 1;
|
||||
static constexpr int EXPANDING = 2;
|
||||
static constexpr int EXPANDED = 3;
|
||||
|
||||
static constexpr int HORIZONTAL = 0;
|
||||
static constexpr int VERTICAL = 1;
|
||||
DECLARE_UIEVENT(void,OnExpansionUpdateListener,float expansionFraction, int state);
|
||||
private:
|
||||
int mDuration = DEFAULT_DURATION;
|
||||
float mParallax;
|
||||
float mExpansion;
|
||||
int mOrientation;
|
||||
int mState;
|
||||
float mTargetExpansion;
|
||||
bool mCanceled;
|
||||
|
||||
Interpolator* mInterpolator;// = new FastOutSlowInInterpolator();
|
||||
ValueAnimator* mAnimator;
|
||||
OnExpansionUpdateListener mListener;
|
||||
private:
|
||||
void initView();
|
||||
void animateSize(int targetExpansion);
|
||||
protected:
|
||||
void onMeasure(int widthMeasureSpec, int heightMeasureSpec)override;
|
||||
public:
|
||||
ExpandableLayout(int w,int h);
|
||||
ExpandableLayout(Context* context,const AttributeSet& attrs);
|
||||
~ExpandableLayout();
|
||||
int getState()const;
|
||||
bool isExpanded()const;
|
||||
void toggle();
|
||||
void toggle(bool animate);
|
||||
void expand();
|
||||
void expand(bool animate);
|
||||
void collapse();
|
||||
void collapse(bool animate);
|
||||
void setExpanded(bool expand);
|
||||
void setExpanded(bool expand, bool animate);
|
||||
int getDuration()const;
|
||||
void setDuration(int duration);
|
||||
void setInterpolator(Interpolator* interpolator);
|
||||
float getExpansion()const;
|
||||
void setExpansion(float expansion);
|
||||
float getParallax()const;
|
||||
void setParallax(float parallax);
|
||||
int getOrientation()const;
|
||||
void setOrientation(int orientation);
|
||||
void setOnExpansionUpdateListener(OnExpansionUpdateListener listener);
|
||||
};
|
||||
}/*endof namespace*/
|
||||
#endif
|
@ -1,322 +0,0 @@
|
||||
#ifndef __EXPANDABLE_LISTADAPTER_H__
|
||||
#define __EXPANDABLE_LISTADAPTER_H__
|
||||
namespace cdroid{
|
||||
|
||||
class HeterogeneousExpandableList {
|
||||
public:
|
||||
/**
|
||||
* Get the type of group View that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . for the specified group item.
|
||||
*
|
||||
* @param groupPosition the position of the group for which the type should be returned.
|
||||
* @return An integer representing the type of group View. Two group views should share the same
|
||||
* type if one can be converted to the other in
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
|
||||
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
|
||||
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
|
||||
* @see #getGroupTypeCount()
|
||||
*/
|
||||
virtual int getGroupType(int groupPosition)=0;
|
||||
|
||||
/**
|
||||
* Get the type of child View that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* for the specified child item.
|
||||
*
|
||||
* @param groupPosition the position of the group that the child resides in
|
||||
* @param childPosition the position of the child with respect to other children in the group
|
||||
* @return An integer representing the type of child View. Two child views should share the same
|
||||
* type if one can be converted to the other in
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
|
||||
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
|
||||
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
|
||||
* @see #getChildTypeCount()
|
||||
*/
|
||||
virtual int getChildType(int groupPosition, int childPosition)=0;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the number of types of group Views that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . Each type represents a set of views that can be converted in
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . If the adapter always returns the same type of View for all group items, this method should
|
||||
* return 1.
|
||||
* </p>
|
||||
* This method will only be called when the adapter is set on the {@link AdapterView}.
|
||||
*
|
||||
* @return The number of types of group Views that will be created by this adapter.
|
||||
* @see #getChildTypeCount()
|
||||
* @see #getGroupType(int)
|
||||
*/
|
||||
virtual int getGroupTypeCount()=0;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Returns the number of types of child Views that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* . Each type represents a set of views that can be converted in
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* , for any group. If the adapter always returns the same type of View for
|
||||
* all child items, this method should return 1.
|
||||
* </p>
|
||||
* This method will only be called when the adapter is set on the {@link AdapterView}.
|
||||
*
|
||||
* @return The total number of types of child Views that will be created by this adapter.
|
||||
* @see #getGroupTypeCount()
|
||||
* @see #getChildType(int, int)
|
||||
*/
|
||||
virtual int getChildTypeCount()=0;
|
||||
};
|
||||
|
||||
class ExpandableListAdapter {
|
||||
public:
|
||||
/**
|
||||
* @see Adapter#registerDataSetObserver(DataSetObserver)
|
||||
*/
|
||||
virtual void registerDataSetObserver(DataSetObserver* observer)=0;
|
||||
|
||||
/**
|
||||
* @see Adapter#unregisterDataSetObserver(DataSetObserver)
|
||||
*/
|
||||
virtual void unregisterDataSetObserver(DataSetObserver* observer)=0;
|
||||
|
||||
/**
|
||||
* Gets the number of groups.
|
||||
*
|
||||
* @return the number of groups
|
||||
*/
|
||||
virtual int getGroupCount()=0;
|
||||
|
||||
/**
|
||||
* Gets the number of children in a specified group.
|
||||
*
|
||||
* @param groupPosition the position of the group for which the children
|
||||
* count should be returned
|
||||
* @return the children count in the specified group
|
||||
*/
|
||||
virtual int getChildrenCount(int groupPosition)=0;
|
||||
|
||||
/**
|
||||
* Gets the data associated with the given group.
|
||||
*
|
||||
* @param groupPosition the position of the group
|
||||
* @return the data child for the specified group
|
||||
*/
|
||||
virtual void* getGroup(int groupPosition)=0;
|
||||
/**
|
||||
* Get the type of group View that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . for the specified group item.
|
||||
*
|
||||
* @param groupPosition the position of the group for which the type should be returned.
|
||||
* @return An integer representing the type of group View. Two group views should share the same
|
||||
* type if one can be converted to the other in
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
|
||||
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
|
||||
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
|
||||
* @see #getGroupTypeCount()
|
||||
*/
|
||||
virtual int getGroupType(int groupPosition)=0;/*====*/
|
||||
/**
|
||||
* <p>
|
||||
* Returns the number of types of group Views that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . Each type represents a set of views that can be converted in
|
||||
* {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
|
||||
* . If the adapter always returns the same type of View for all group items, this method should
|
||||
* return 1.
|
||||
* </p>
|
||||
* This method will only be called when the adapter is set on the {@link AdapterView}.
|
||||
*
|
||||
* @return The number of types of group Views that will be created by this adapter.
|
||||
* @see #getChildTypeCount()
|
||||
* @see #getGroupType(int)
|
||||
*/
|
||||
|
||||
virtual int getGroupTypeCount()=0;/*====*/
|
||||
/**
|
||||
* Gets the data associated with the given child within the given group.
|
||||
*
|
||||
* @param groupPosition the position of the group that the child resides in
|
||||
* @param childPosition the position of the child with respect to other
|
||||
* children in the group
|
||||
* @return the data of the child
|
||||
*/
|
||||
virtual void* getChild(int groupPosition, int childPosition)=0;
|
||||
/**
|
||||
* Get the type of child View that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* for the specified child item.
|
||||
*
|
||||
* @param groupPosition the position of the group that the child resides in
|
||||
* @param childPosition the position of the child with respect to other children in the group
|
||||
* @return An integer representing the type of child View. Two child views should share the same
|
||||
* type if one can be converted to the other in
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
|
||||
* {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
|
||||
* @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
|
||||
* @see #getChildTypeCount()
|
||||
*/
|
||||
virtual int getChildType(int groupPosition, int childPosition)=0;/*===*/
|
||||
/**
|
||||
* <p>
|
||||
* Returns the number of types of child Views that will be created by
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* . Each type represents a set of views that can be converted in
|
||||
* {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
|
||||
* , for any group. If the adapter always returns the same type of View for
|
||||
* all child items, this method should return 1.
|
||||
* </p>
|
||||
* This method will only be called when the adapter is set on the {@link AdapterView}.
|
||||
*
|
||||
* @return The total number of types of child Views that will be created by this adapter.
|
||||
* @see #getGroupTypeCount()
|
||||
* @see #getChildType(int, int)
|
||||
*/
|
||||
|
||||
virtual int getChildTypeCount()=0;/*===*/
|
||||
/**
|
||||
* Gets the ID for the group at the given position. This group ID must be
|
||||
* unique across groups. The combined ID (see
|
||||
* {@link #getCombinedGroupId(long)}) must be unique across ALL items
|
||||
* (groups and all children).
|
||||
*
|
||||
* @param groupPosition the position of the group for which the ID is wanted
|
||||
* @return the ID associated with the group
|
||||
*/
|
||||
virtual long getGroupId(int groupPosition)=0;
|
||||
|
||||
/**
|
||||
* Gets the ID for the given child within the given group. This ID must be
|
||||
* unique across all children within the group. The combined ID (see
|
||||
* {@link #getCombinedChildId(long, long)}) must be unique across ALL items
|
||||
* (groups and all children).
|
||||
*
|
||||
* @param groupPosition the position of the group that contains the child
|
||||
* @param childPosition the position of the child within the group for which
|
||||
* the ID is wanted
|
||||
* @return the ID associated with the child
|
||||
*/
|
||||
virtual long getChildId(int groupPosition, int childPosition)=0;
|
||||
|
||||
/**
|
||||
* Indicates whether the child and group IDs are stable across changes to the
|
||||
* underlying data.
|
||||
*
|
||||
* @return whether or not the same ID always refers to the same object
|
||||
* @see Adapter#hasStableIds()
|
||||
*/
|
||||
virtual bool hasStableIds()=0;
|
||||
|
||||
/**
|
||||
* Gets a View that displays the given group. This View is only for the
|
||||
* group--the Views for the group's children will be fetched using
|
||||
* {@link #getChildView(int, int, boolean, View, ViewGroup)}.
|
||||
*
|
||||
* @param groupPosition the position of the group for which the View is
|
||||
* returned
|
||||
* @param isExpanded whether the group is expanded or collapsed
|
||||
* @param convertView the old view to reuse, if possible. You should check
|
||||
* that this view is non-null and of an appropriate type before
|
||||
* using. If it is not possible to convert this view to display
|
||||
* the correct data, this method can create a new view. It is not
|
||||
* guaranteed that the convertView will have been previously
|
||||
* created by
|
||||
* {@link #getGroupView(int, boolean, View, ViewGroup)}.
|
||||
* @param parent the parent that this view will eventually be attached to
|
||||
* @return the View corresponding to the group at the specified position
|
||||
*/
|
||||
virtual View* getGroupView(int groupPosition, bool isExpanded, View* convertView, ViewGroup* parent)=0;
|
||||
|
||||
/**
|
||||
* Gets a View that displays the data for the given child within the given
|
||||
* group.
|
||||
*
|
||||
* @param groupPosition the position of the group that contains the child
|
||||
* @param childPosition the position of the child (for which the View is
|
||||
* returned) within the group
|
||||
* @param isLastChild Whether the child is the last child within the group
|
||||
* @param convertView the old view to reuse, if possible. You should check
|
||||
* that this view is non-null and of an appropriate type before
|
||||
* using. If it is not possible to convert this view to display
|
||||
* the correct data, this method can create a new view. It is not
|
||||
* guaranteed that the convertView will have been previously
|
||||
* created by
|
||||
* {@link #getChildView(int, int, boolean, View, ViewGroup)}.
|
||||
* @param parent the parent that this view will eventually be attached to
|
||||
* @return the View corresponding to the child at the specified position
|
||||
*/
|
||||
virtual View* getChildView(int groupPosition, int childPosition, bool isLastChild,
|
||||
View* convertView, ViewGroup* parent)=0;
|
||||
|
||||
/**
|
||||
* Whether the child at the specified position is selectable.
|
||||
*
|
||||
* @param groupPosition the position of the group that contains the child
|
||||
* @param childPosition the position of the child within the group
|
||||
* @return whether the child is selectable.
|
||||
*/
|
||||
virtual bool isChildSelectable(int groupPosition, int childPosition)=0;
|
||||
|
||||
/**
|
||||
* @see ListAdapter#areAllItemsEnabled()
|
||||
*/
|
||||
virtual bool areAllItemsEnabled()=0;
|
||||
|
||||
/**
|
||||
* @see ListAdapter#isEmpty()
|
||||
*/
|
||||
virtual bool isEmpty()=0;
|
||||
|
||||
/**
|
||||
* Called when a group is expanded.
|
||||
*
|
||||
* @param groupPosition The group being expanded.
|
||||
*/
|
||||
virtual void onGroupExpanded(int groupPosition)=0;
|
||||
|
||||
/**
|
||||
* Called when a group is collapsed.
|
||||
*
|
||||
* @param groupPosition The group being collapsed.
|
||||
*/
|
||||
virtual void onGroupCollapsed(int groupPosition)=0;
|
||||
|
||||
/**
|
||||
* Gets an ID for a child that is unique across any item (either group or
|
||||
* child) that is in this list. Expandable lists require each item (group or
|
||||
* child) to have a unique ID among all children and groups in the list.
|
||||
* This method is responsible for returning that unique ID given a child's
|
||||
* ID and its group's ID. Furthermore, if {@link #hasStableIds()} is true, the
|
||||
* returned ID must be stable as well.
|
||||
*
|
||||
* @param groupId The ID of the group that contains this child.
|
||||
* @param childId The ID of the child.
|
||||
* @return The unique (and possibly stable) ID of the child across all
|
||||
* groups and children in this list.
|
||||
*/
|
||||
virtual int64_t getCombinedChildId(long groupId, long childId)=0;
|
||||
|
||||
/**
|
||||
* Gets an ID for a group that is unique across any item (either group or
|
||||
* child) that is in this list. Expandable lists require each item (group or
|
||||
* child) to have a unique ID among all children and groups in the list.
|
||||
* This method is responsible for returning that unique ID given a group's
|
||||
* ID. Furthermore, if {@link #hasStableIds()} is true, the returned ID must be
|
||||
* stable as well.
|
||||
*
|
||||
* @param groupId The ID of the group
|
||||
* @return The unique (and possibly stable) ID of the group across all
|
||||
* groups and children in this list.
|
||||
*/
|
||||
virtual int64_t getCombinedGroupId(long groupId)=0;
|
||||
};
|
||||
}/*endof namespace*/
|
||||
|
||||
#endif/*__EXPANDABLE_LISTADAPTER_H__*/
|
@ -1,610 +0,0 @@
|
||||
#include <widget/expandablelistconnector.h>
|
||||
#include <widget/adapterview.h>
|
||||
|
||||
namespace cdroid{
|
||||
|
||||
ExpandableListConnector::ExpandableListConnector(ExpandableListAdapter* expandableListAdapter){
|
||||
mExpandableListAdapter = nullptr;
|
||||
mTotalExpChildrenCount = 0;
|
||||
mDataSetObserver = new MyDataSetObserver(this);
|
||||
setExpandableListAdapter(expandableListAdapter);
|
||||
}
|
||||
|
||||
ExpandableListConnector::~ExpandableListConnector(){
|
||||
delete mDataSetObserver;
|
||||
}
|
||||
|
||||
void ExpandableListConnector::setExpandableListAdapter(ExpandableListAdapter* expandableListAdapter) {
|
||||
if (mExpandableListAdapter ) {
|
||||
mExpandableListAdapter->unregisterDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
mExpandableListAdapter = expandableListAdapter;
|
||||
expandableListAdapter->registerDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
|
||||
ExpandableListConnector::PositionMetadata* ExpandableListConnector::getUnflattenedPos(int flPos) const{
|
||||
/* Keep locally since frequent use */
|
||||
const std::vector<GroupMetadata*>& egml = mExpGroupMetadataList;
|
||||
const int numExpGroups = egml.size();
|
||||
|
||||
/* Binary search variables */
|
||||
int leftExpGroupIndex = 0;
|
||||
int rightExpGroupIndex = numExpGroups - 1;
|
||||
int midExpGroupIndex = 0;
|
||||
GroupMetadata* midExpGm;
|
||||
|
||||
if (numExpGroups == 0) {
|
||||
return PositionMetadata::obtain(flPos, ExpandableListPosition::GROUP, flPos, -1, nullptr, 0);
|
||||
}
|
||||
|
||||
while (leftExpGroupIndex <= rightExpGroupIndex) {
|
||||
midExpGroupIndex = (rightExpGroupIndex - leftExpGroupIndex) / 2 + leftExpGroupIndex;
|
||||
midExpGm = egml.at(midExpGroupIndex);
|
||||
|
||||
if (flPos > midExpGm->lastChildFlPos) {
|
||||
leftExpGroupIndex = midExpGroupIndex + 1;
|
||||
} else if (flPos < midExpGm->flPos) {
|
||||
rightExpGroupIndex = midExpGroupIndex - 1;
|
||||
} else if (flPos == midExpGm->flPos) {
|
||||
return PositionMetadata::obtain(flPos, ExpandableListPosition::GROUP,
|
||||
midExpGm->gPos, -1, midExpGm, midExpGroupIndex);
|
||||
} else if (flPos <= midExpGm->lastChildFlPos
|
||||
/* && flPos > midGm.flPos as deduced from previous
|
||||
* conditions */) {
|
||||
const int childPos = flPos - (midExpGm->flPos + 1);
|
||||
return PositionMetadata::obtain(flPos, ExpandableListPosition::CHILD,
|
||||
midExpGm->gPos, childPos, midExpGm, midExpGroupIndex);
|
||||
}
|
||||
}
|
||||
|
||||
int insertPosition = 0;
|
||||
|
||||
/** What is its group position in the list of all groups? */
|
||||
int groupPos = 0;
|
||||
|
||||
if (leftExpGroupIndex > midExpGroupIndex) {
|
||||
const GroupMetadata* leftExpGm = egml.at(leftExpGroupIndex-1);
|
||||
insertPosition = leftExpGroupIndex;
|
||||
groupPos = (flPos - leftExpGm->lastChildFlPos) + leftExpGm->gPos;
|
||||
} else if (rightExpGroupIndex < midExpGroupIndex) {
|
||||
const GroupMetadata* rightExpGm = egml.at(++rightExpGroupIndex);
|
||||
insertPosition = rightExpGroupIndex;
|
||||
|
||||
groupPos = rightExpGm->gPos - (rightExpGm->flPos - flPos);
|
||||
} else {
|
||||
// TODO: clean exit
|
||||
FATAL("Unknown state");
|
||||
}
|
||||
return PositionMetadata::obtain(flPos, ExpandableListPosition::GROUP, groupPos, -1,
|
||||
nullptr, insertPosition);
|
||||
}
|
||||
|
||||
ExpandableListConnector::PositionMetadata* ExpandableListConnector::getFlattenedPos(const ExpandableListPosition& pos) {
|
||||
const std::vector<GroupMetadata*>& egml = mExpGroupMetadataList;
|
||||
const int numExpGroups = egml.size();
|
||||
|
||||
/* Binary search variables */
|
||||
int leftExpGroupIndex = 0;
|
||||
int rightExpGroupIndex = numExpGroups - 1;
|
||||
int midExpGroupIndex = 0;
|
||||
GroupMetadata* midExpGm;
|
||||
|
||||
if (numExpGroups == 0) {
|
||||
return PositionMetadata::obtain(pos.groupPos, pos.type, pos.groupPos, pos.childPos, nullptr, 0);
|
||||
}
|
||||
|
||||
while (leftExpGroupIndex <= rightExpGroupIndex) {
|
||||
midExpGroupIndex = (rightExpGroupIndex - leftExpGroupIndex)/2 + leftExpGroupIndex;
|
||||
midExpGm = egml.at(midExpGroupIndex);
|
||||
|
||||
if (pos.groupPos > midExpGm->gPos) {
|
||||
leftExpGroupIndex = midExpGroupIndex + 1;
|
||||
} else if (pos.groupPos < midExpGm->gPos) {
|
||||
rightExpGroupIndex = midExpGroupIndex - 1;
|
||||
} else if (pos.groupPos == midExpGm->gPos) {
|
||||
if (pos.type == ExpandableListPosition::GROUP) {
|
||||
/* If it's a group, give them this matched group's flPos */
|
||||
return PositionMetadata::obtain(midExpGm->flPos, pos.type,
|
||||
pos.groupPos, pos.childPos, midExpGm, midExpGroupIndex);
|
||||
} else if (pos.type == ExpandableListPosition::CHILD) {
|
||||
/* If it's a child, calculate the flat list pos */
|
||||
return PositionMetadata::obtain(midExpGm->flPos + pos.childPos
|
||||
+ 1, pos.type, pos.groupPos, pos.childPos,
|
||||
midExpGm, midExpGroupIndex);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pos.type != ExpandableListPosition::GROUP) {
|
||||
/* If it isn't a group, return null */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (leftExpGroupIndex > midExpGroupIndex) {
|
||||
|
||||
const GroupMetadata* leftExpGm = egml.at(leftExpGroupIndex-1);
|
||||
const int flPos = leftExpGm->lastChildFlPos + (pos.groupPos - leftExpGm->gPos);
|
||||
|
||||
return PositionMetadata::obtain(flPos, pos.type, pos.groupPos,
|
||||
pos.childPos, nullptr, leftExpGroupIndex);
|
||||
} else if (rightExpGroupIndex < midExpGroupIndex) {
|
||||
const GroupMetadata* rightExpGm = egml.at(++rightExpGroupIndex);
|
||||
const int flPos = rightExpGm->flPos - (rightExpGm->gPos - pos.groupPos);
|
||||
return PositionMetadata::obtain(flPos, pos.type, pos.groupPos,
|
||||
pos.childPos, nullptr, rightExpGroupIndex);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::areAllItemsEnabled() {
|
||||
return mExpandableListAdapter->areAllItemsEnabled();
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::isEnabled(int flatListPos) {
|
||||
PositionMetadata* metadata = getUnflattenedPos(flatListPos);
|
||||
ExpandableListPosition* pos = metadata->position;
|
||||
|
||||
bool retValue;
|
||||
if (pos->type == ExpandableListPosition::CHILD) {
|
||||
retValue = mExpandableListAdapter->isChildSelectable(pos->groupPos, pos->childPos);
|
||||
} else {
|
||||
// Groups are always selectable
|
||||
retValue = true;
|
||||
}
|
||||
|
||||
metadata->recycle();
|
||||
return retValue;
|
||||
}
|
||||
|
||||
int ExpandableListConnector::getCount()const{
|
||||
/* Total count for the list view is the number groups plus the
|
||||
* number of children from currently expanded groups (a value we keep
|
||||
* cached in this class)*/
|
||||
return mExpandableListAdapter->getGroupCount() + mTotalExpChildrenCount;
|
||||
}
|
||||
|
||||
void* ExpandableListConnector::getItem(int flatListPos) const{
|
||||
PositionMetadata* posMetadata = getUnflattenedPos(flatListPos);
|
||||
|
||||
void* retValue;/*Object*/
|
||||
if (posMetadata->position->type == ExpandableListPosition::GROUP) {
|
||||
retValue = mExpandableListAdapter->getGroup(posMetadata->position->groupPos);
|
||||
} else if (posMetadata->position->type == ExpandableListPosition::CHILD) {
|
||||
retValue = mExpandableListAdapter->getChild(posMetadata->position->groupPos,
|
||||
posMetadata->position->childPos);
|
||||
} else {
|
||||
// TODO: clean exit
|
||||
FATAL("Flat list position is of unknown type");
|
||||
}
|
||||
|
||||
posMetadata->recycle();
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
long ExpandableListConnector::getItemId(int flatListPos) {
|
||||
PositionMetadata* posMetadata = getUnflattenedPos(flatListPos);
|
||||
const long groupId = mExpandableListAdapter->getGroupId(posMetadata->position->groupPos);
|
||||
|
||||
long retValue;
|
||||
if (posMetadata->position->type == ExpandableListPosition::GROUP) {
|
||||
retValue = mExpandableListAdapter->getCombinedGroupId(groupId);
|
||||
} else if (posMetadata->position->type == ExpandableListPosition::CHILD) {
|
||||
const long childId = mExpandableListAdapter->getChildId(posMetadata->position->groupPos,
|
||||
posMetadata->position->childPos);
|
||||
retValue = mExpandableListAdapter->getCombinedChildId(groupId, childId);
|
||||
} else {
|
||||
// TODO: clean exit
|
||||
FATAL("Flat list position is of unknown type");
|
||||
}
|
||||
|
||||
posMetadata->recycle();
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
View* ExpandableListConnector::getView(int flatListPos, View* convertView, ViewGroup* parent){
|
||||
View* retValue;
|
||||
PositionMetadata* posMetadata = getUnflattenedPos(flatListPos);
|
||||
if (posMetadata->position->type == ExpandableListPosition::GROUP) {
|
||||
retValue = mExpandableListAdapter->getGroupView(posMetadata->position->groupPos,
|
||||
posMetadata->isExpanded(), convertView, parent);
|
||||
} else if (posMetadata->position->type == ExpandableListPosition::CHILD) {
|
||||
const bool isLastChild = posMetadata->groupMetadata->lastChildFlPos == flatListPos;
|
||||
retValue = mExpandableListAdapter->getChildView(posMetadata->position->groupPos,
|
||||
posMetadata->position->childPos, isLastChild, convertView, parent);
|
||||
} else {
|
||||
// TODO: clean exit
|
||||
FATAL("Flat list position is of unknown type");
|
||||
}
|
||||
posMetadata->recycle();
|
||||
return retValue;
|
||||
}
|
||||
|
||||
int ExpandableListConnector::getItemViewType(int flatListPos)const {
|
||||
PositionMetadata* metadata = getUnflattenedPos(flatListPos);
|
||||
const ExpandableListPosition* pos = metadata->position;
|
||||
|
||||
int retValue;
|
||||
auto adapter = mExpandableListAdapter;
|
||||
if (pos->type == ExpandableListPosition::GROUP) {
|
||||
retValue = adapter->getGroupType(pos->groupPos);
|
||||
} else {
|
||||
const int childType = adapter->getChildType(pos->groupPos, pos->childPos);
|
||||
retValue = adapter->getGroupTypeCount() + childType;
|
||||
}
|
||||
metadata->recycle();
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
int ExpandableListConnector::getViewTypeCount()const{
|
||||
auto adapter = mExpandableListAdapter;
|
||||
return adapter->getGroupTypeCount() + adapter->getChildTypeCount();
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::hasStableIds() const{
|
||||
return mExpandableListAdapter->hasStableIds();
|
||||
}
|
||||
|
||||
void ExpandableListConnector::refreshExpGroupMetadataList(bool forceChildrenCountRefresh, bool syncGroupPositions){
|
||||
std::vector<GroupMetadata*>& egml = mExpGroupMetadataList;
|
||||
int egmlSize = egml.size();
|
||||
int curFlPos = 0;
|
||||
|
||||
/* Update child count as we go through */
|
||||
mTotalExpChildrenCount = 0;
|
||||
|
||||
if (syncGroupPositions) {
|
||||
// We need to check whether any groups have moved positions
|
||||
bool positionsChanged = false;
|
||||
|
||||
for (int i = egmlSize - 1; i >= 0; i--) {
|
||||
GroupMetadata* curGm = egml.at(i);
|
||||
int newGPos = findGroupPosition(curGm->gId, curGm->gPos);
|
||||
if (newGPos != curGm->gPos) {
|
||||
if (newGPos == AdapterView::INVALID_POSITION) {
|
||||
// Doh, just remove it from the list of expanded groups
|
||||
egml.erase(egml.begin()+i);//remove(i);
|
||||
egmlSize--;
|
||||
}
|
||||
|
||||
curGm->gPos = newGPos;
|
||||
if (!positionsChanged) positionsChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (positionsChanged) {
|
||||
// At least one group changed positions, so re-sort
|
||||
//Collections.sort(egml);
|
||||
std::sort(egml.begin(),egml.end(), [](GroupMetadata*lhs,GroupMetadata*rhs)->int{
|
||||
return lhs->gPos-rhs->gPos;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int gChildrenCount;
|
||||
int lastGPos = 0;
|
||||
for (int i = 0; i < egmlSize; i++) {
|
||||
/* Store in local variable since we'll access freq */
|
||||
GroupMetadata* curGm = egml.at(i);
|
||||
|
||||
/* Get the number of children, try to refrain from calling
|
||||
* another class's method unless we have to (so do a subtraction) */
|
||||
if ((curGm->lastChildFlPos == GroupMetadata::REFRESH) || forceChildrenCountRefresh) {
|
||||
gChildrenCount = mExpandableListAdapter->getChildrenCount(curGm->gPos);
|
||||
} else {
|
||||
/* Num children for this group is its last child's fl pos minus
|
||||
* the group's fl pos */
|
||||
gChildrenCount = curGm->lastChildFlPos - curGm->flPos;
|
||||
}
|
||||
|
||||
/* Update */
|
||||
mTotalExpChildrenCount += gChildrenCount;
|
||||
|
||||
curFlPos += (curGm->gPos - lastGPos);
|
||||
lastGPos = curGm->gPos;
|
||||
|
||||
/* Update the flat list positions, and the current flat list pos */
|
||||
curGm->flPos = curFlPos;
|
||||
curFlPos += gChildrenCount;
|
||||
curGm->lastChildFlPos = curFlPos;
|
||||
}
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::collapseGroup(int groupPos) {
|
||||
ExpandableListPosition* elGroupPos = ExpandableListPosition::obtain(
|
||||
ExpandableListPosition::GROUP, groupPos, -1, -1);
|
||||
PositionMetadata* pm = getFlattenedPos(*elGroupPos);
|
||||
elGroupPos->recycle();
|
||||
if (pm == nullptr) return false;
|
||||
|
||||
bool retValue = collapseGroup(*pm);
|
||||
pm->recycle();
|
||||
return retValue;
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::collapseGroup(PositionMetadata& posMetadata) {
|
||||
/*
|
||||
* Collapsing requires removal from mExpGroupMetadataList
|
||||
*/
|
||||
|
||||
/*
|
||||
* If it is null, it must be already collapsed. This group metadata
|
||||
* object should have been set from the search that returned the
|
||||
* position metadata object.
|
||||
*/
|
||||
if (posMetadata.groupMetadata == nullptr) return false;
|
||||
|
||||
// Remove the group from the list of expanded groups
|
||||
auto it = std::find(mExpGroupMetadataList.begin(),mExpGroupMetadataList.end(),posMetadata.groupMetadata);
|
||||
mExpGroupMetadataList.erase(it);//remove(posMetadata.groupMetadata);
|
||||
|
||||
// Refresh the metadata
|
||||
refreshExpGroupMetadataList(false, false);
|
||||
|
||||
// Notify of change
|
||||
notifyDataSetChanged();
|
||||
|
||||
// Give the callback
|
||||
mExpandableListAdapter->onGroupCollapsed(posMetadata.groupMetadata->gPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::expandGroup(int groupPos) {
|
||||
ExpandableListPosition* elGroupPos = ExpandableListPosition::obtain(
|
||||
ExpandableListPosition::GROUP, groupPos, -1, -1);
|
||||
PositionMetadata* pm = getFlattenedPos(*elGroupPos);
|
||||
elGroupPos->recycle();
|
||||
const bool retValue = expandGroup(*pm);
|
||||
pm->recycle();
|
||||
return retValue;
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::expandGroup(PositionMetadata& posMetadata) {
|
||||
/*
|
||||
* Expanding requires insertion into the mExpGroupMetadataList
|
||||
*/
|
||||
|
||||
if (posMetadata.position->groupPos < 0) {
|
||||
// TODO clean exit
|
||||
FATAL("Need group");
|
||||
}
|
||||
|
||||
if (mMaxExpGroupCount == 0) return false;
|
||||
|
||||
// Check to see if it's already expanded
|
||||
if (posMetadata.groupMetadata != nullptr) return false;
|
||||
|
||||
/* Restrict number of expanded groups to mMaxExpGroupCount */
|
||||
if (mExpGroupMetadataList.size() >= mMaxExpGroupCount) {
|
||||
/* Collapse a group */
|
||||
// TODO: Collapse something not on the screen instead of the first one?
|
||||
// TODO: Could write overloaded function to take GroupMetadata to collapse
|
||||
GroupMetadata* collapsedGm = mExpGroupMetadataList.at(0);
|
||||
|
||||
auto it = std::find(mExpGroupMetadataList.begin(),mExpGroupMetadataList.end(),collapsedGm);
|
||||
int collapsedIndex = it - mExpGroupMetadataList.begin();//mExpGroupMetadataList.indexOf(collapsedGm);
|
||||
|
||||
collapseGroup(collapsedGm->gPos);
|
||||
|
||||
/* Decrement index if it is after the group we removed */
|
||||
if (posMetadata.groupInsertIndex > collapsedIndex) {
|
||||
posMetadata.groupInsertIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
GroupMetadata* expandedGm = GroupMetadata::obtain(GroupMetadata::REFRESH,GroupMetadata::REFRESH,
|
||||
posMetadata.position->groupPos, mExpandableListAdapter->getGroupId(posMetadata.position->groupPos));
|
||||
|
||||
mExpGroupMetadataList.insert(mExpGroupMetadataList.begin() + posMetadata.groupInsertIndex, expandedGm);
|
||||
|
||||
// Refresh the metadata
|
||||
refreshExpGroupMetadataList(false, false);
|
||||
|
||||
// Notify of change
|
||||
notifyDataSetChanged();
|
||||
|
||||
// Give the callback
|
||||
mExpandableListAdapter->onGroupExpanded(expandedGm->gPos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::isGroupExpanded(int groupPosition) {
|
||||
GroupMetadata* groupMetadata;
|
||||
for (int i = mExpGroupMetadataList.size() - 1; i >= 0; i--) {
|
||||
groupMetadata = mExpGroupMetadataList.at(i);
|
||||
if (groupMetadata->gPos == groupPosition) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExpandableListConnector::setMaxExpGroupCount(int maxExpGroupCount) {
|
||||
mMaxExpGroupCount = maxExpGroupCount;
|
||||
}
|
||||
|
||||
ExpandableListAdapter* ExpandableListConnector::getAdapter() {
|
||||
return mExpandableListAdapter;
|
||||
}
|
||||
|
||||
Filter* ExpandableListConnector::getFilter() {
|
||||
ExpandableListAdapter* adapter = getAdapter();
|
||||
if (dynamic_cast<Filterable*>(adapter)) {
|
||||
return ((Filterable*) adapter)->getFilter();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ExpandableListConnector::GroupMetadata*>& ExpandableListConnector::getExpandedGroupMetadataList() {
|
||||
return mExpGroupMetadataList;
|
||||
}
|
||||
|
||||
void ExpandableListConnector::setExpandedGroupMetadataList(std::vector<GroupMetadata*>& expandedGroupMetadataList) {
|
||||
|
||||
if (mExpandableListAdapter == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure our current data set is big enough for the previously
|
||||
// expanded groups, if not, ignore this request
|
||||
const int numGroups = mExpandableListAdapter->getGroupCount();
|
||||
for (int i = expandedGroupMetadataList.size() - 1; i >= 0; i--) {
|
||||
if (expandedGroupMetadataList.at(i)->gPos >= numGroups) {
|
||||
// Doh, for some reason the client doesn't have some of the groups
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mExpGroupMetadataList = expandedGroupMetadataList;
|
||||
refreshExpGroupMetadataList(true, false);
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::isEmpty() {
|
||||
ExpandableListAdapter* adapter = getAdapter();
|
||||
return adapter ? adapter->isEmpty() : true;
|
||||
}
|
||||
|
||||
int ExpandableListConnector::findGroupPosition(long groupIdToMatch, int seedGroupPosition) {
|
||||
const int count = mExpandableListAdapter->getGroupCount();
|
||||
|
||||
if (count == 0) {
|
||||
return AdapterView::INVALID_POSITION;
|
||||
}
|
||||
|
||||
// If there isn't a selection don't hunt for it
|
||||
if (groupIdToMatch == AdapterView::INVALID_ROW_ID) {
|
||||
return AdapterView::INVALID_POSITION;
|
||||
}
|
||||
|
||||
// Pin seed to reasonable values
|
||||
seedGroupPosition = std::max(0, seedGroupPosition);
|
||||
seedGroupPosition = std::min(count - 1, seedGroupPosition);
|
||||
|
||||
long endTime = SystemClock::uptimeMillis() + AdapterView::SYNC_MAX_DURATION_MILLIS;
|
||||
|
||||
long rowId;
|
||||
|
||||
// first position scanned so far
|
||||
int first = seedGroupPosition;
|
||||
|
||||
// last position scanned so far
|
||||
int last = seedGroupPosition;
|
||||
|
||||
// True if we should move down on the next iteration
|
||||
bool next = false;
|
||||
|
||||
// True when we have looked at the first item in the data
|
||||
bool hitFirst;
|
||||
|
||||
// True when we have looked at the last item in the data
|
||||
bool hitLast;
|
||||
|
||||
// Get the item ID locally (instead of getItemIdAtPosition), so
|
||||
// we need the adapter
|
||||
ExpandableListAdapter* adapter = getAdapter();
|
||||
if (adapter == nullptr) {
|
||||
return AdapterView::INVALID_POSITION;
|
||||
}
|
||||
|
||||
while (SystemClock::uptimeMillis() <= endTime) {
|
||||
rowId = adapter->getGroupId(seedGroupPosition);
|
||||
if (rowId == groupIdToMatch) {
|
||||
// Found it!
|
||||
return seedGroupPosition;
|
||||
}
|
||||
|
||||
hitLast = last == count - 1;
|
||||
hitFirst = first == 0;
|
||||
|
||||
if (hitLast && hitFirst) {
|
||||
// Looked at everything
|
||||
break;
|
||||
}
|
||||
|
||||
if (hitFirst || (next && !hitLast)) {
|
||||
// Either we hit the top, or we are trying to move down
|
||||
last++;
|
||||
seedGroupPosition = last;
|
||||
// Try going up next time
|
||||
next = false;
|
||||
} else if (hitLast || (!next && !hitFirst)) {
|
||||
// Either we hit the bottom, or we are trying to move up
|
||||
first--;
|
||||
seedGroupPosition = first;
|
||||
// Try going down next time
|
||||
next = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return AdapterView::INVALID_POSITION;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::queue<ExpandableListConnector::PositionMetadata*> ExpandableListConnector::PositionMetadata::sPool;
|
||||
|
||||
ExpandableListConnector::GroupMetadata*
|
||||
ExpandableListConnector::GroupMetadata::obtain(int flPos, int lastChildFlPos, int gPos, long gId) {
|
||||
GroupMetadata* gm = new GroupMetadata();
|
||||
gm->flPos = flPos;
|
||||
gm->lastChildFlPos = lastChildFlPos;
|
||||
gm->gPos = gPos;
|
||||
gm->gId = gId;
|
||||
return gm;
|
||||
}
|
||||
|
||||
void ExpandableListConnector::PositionMetadata::resetState() {
|
||||
if (position != nullptr) {
|
||||
position->recycle();
|
||||
position = nullptr;
|
||||
}
|
||||
groupMetadata = nullptr;
|
||||
groupInsertIndex = 0;
|
||||
}
|
||||
|
||||
ExpandableListConnector::PositionMetadata::PositionMetadata() {
|
||||
}
|
||||
|
||||
ExpandableListConnector::PositionMetadata*
|
||||
ExpandableListConnector::PositionMetadata::obtain(int flatListPos, int type, int groupPos,
|
||||
int childPos, GroupMetadata* groupMetadata, int groupInsertIndex) {
|
||||
PositionMetadata* pm = getRecycledOrCreate();
|
||||
pm->position = ExpandableListPosition::obtain(type, groupPos, childPos, flatListPos);
|
||||
pm->groupMetadata = groupMetadata;
|
||||
pm->groupInsertIndex = groupInsertIndex;
|
||||
return pm;
|
||||
}
|
||||
|
||||
ExpandableListConnector::PositionMetadata* ExpandableListConnector::PositionMetadata::getRecycledOrCreate() {
|
||||
PositionMetadata* pm;
|
||||
if (sPool.size() > 0) {
|
||||
pm = sPool.front();
|
||||
sPool.pop();
|
||||
} else {
|
||||
return new PositionMetadata();
|
||||
}
|
||||
pm->resetState();
|
||||
return pm;
|
||||
}
|
||||
|
||||
void ExpandableListConnector::PositionMetadata::recycle(){
|
||||
resetState();
|
||||
if (sPool.size() < MAX_POOL_SIZE) {
|
||||
sPool.push((PositionMetadata*)this);
|
||||
}
|
||||
}
|
||||
|
||||
bool ExpandableListConnector::PositionMetadata::isExpanded() const{
|
||||
return groupMetadata != nullptr;
|
||||
}
|
||||
|
||||
}/*endof namespace*/
|
@ -1,123 +0,0 @@
|
||||
#ifndef __EXPANDABLE_LIST_CONNECTIOR_H__
|
||||
#define __EXPANDABLE_LIST_CONNECTIOR_H__
|
||||
#include <queue>
|
||||
#include <widget/adapter.h>
|
||||
#include <widget/filterable.h>
|
||||
#include <widget/expandablelistposition.h>
|
||||
#include <widget/expandablelistadapter.h>
|
||||
|
||||
namespace cdroid{
|
||||
class ExpandableListConnector:public BaseAdapter,public Filterable{
|
||||
protected:
|
||||
class MyDataSetObserver:public DataSetObserver {
|
||||
ExpandableListConnector*mConnector;
|
||||
public:
|
||||
MyDataSetObserver(ExpandableListConnector*connector){
|
||||
mConnector = connector;
|
||||
}
|
||||
void onChanged() {
|
||||
mConnector->refreshExpGroupMetadataList(true, true);
|
||||
mConnector->notifyDataSetChanged();
|
||||
}
|
||||
void onInvalidated() {
|
||||
mConnector->refreshExpGroupMetadataList(true, true);
|
||||
mConnector->notifyDataSetInvalidated();
|
||||
}
|
||||
void clearSavedState()override{}
|
||||
};
|
||||
public:
|
||||
class GroupMetadata{
|
||||
public:
|
||||
static constexpr int REFRESH = -1;
|
||||
/** This group's flat list position */
|
||||
int flPos;
|
||||
/* firstChildFlPos isn't needed since it's (flPos + 1) */
|
||||
/* This group's last child's flat list position, so basically
|
||||
* the range of this group in the flat list */
|
||||
int lastChildFlPos;
|
||||
/* This group's group position */
|
||||
int gPos;
|
||||
/* This group's id */
|
||||
long gId;
|
||||
public:
|
||||
static GroupMetadata* obtain(int flPos, int lastChildFlPos, int gPos, long gId);
|
||||
};
|
||||
class PositionMetadata {
|
||||
private:
|
||||
static constexpr int MAX_POOL_SIZE = 5;
|
||||
static std::queue<PositionMetadata*> sPool;
|
||||
/** Data type to hold the position and its type (child/group) */
|
||||
void resetState();
|
||||
PositionMetadata();
|
||||
public:
|
||||
ExpandableListPosition* position;
|
||||
/* Link back to the expanded GroupMetadata for this group. Useful for
|
||||
* removing the group from the list of expanded groups inside the
|
||||
* connector when we collapse the group, and also as a check to see if
|
||||
* the group was expanded or collapsed (this will be null if the group
|
||||
* is collapsed since we don't keep that group's metadata)*/
|
||||
GroupMetadata* groupMetadata;
|
||||
|
||||
/* For groups that are collapsed, we use this as the index (in
|
||||
* mExpGroupMetadataList) to insert this group when we are expanding
|
||||
* this group.*/
|
||||
int groupInsertIndex;
|
||||
static PositionMetadata* obtain(int flatListPos, int type, int groupPos,
|
||||
int childPos, GroupMetadata* groupMetadata, int groupInsertIndex);
|
||||
static PositionMetadata* getRecycledOrCreate() ;
|
||||
void recycle();
|
||||
bool isExpanded()const;
|
||||
};
|
||||
private:
|
||||
ExpandableListAdapter* mExpandableListAdapter;
|
||||
/**
|
||||
* List of metadata for the currently expanded groups. The metadata consists
|
||||
* of data essential for efficiently translating between flat list positions
|
||||
* and group/child positions. See {@link GroupMetadata}.
|
||||
*/
|
||||
std::vector<GroupMetadata*> mExpGroupMetadataList;
|
||||
|
||||
/** The number of children from all currently expanded groups */
|
||||
int mTotalExpChildrenCount;
|
||||
|
||||
/** The maximum number of allowable expanded groups. Defaults to 'no limit' */
|
||||
int mMaxExpGroupCount = INT_MAX;
|
||||
|
||||
/** Change observer used to have ExpandableListAdapter changes pushed to us */
|
||||
DataSetObserver* mDataSetObserver;
|
||||
private:
|
||||
void refreshExpGroupMetadataList(bool forceChildrenCountRefresh, bool syncGroupPositions);
|
||||
public:
|
||||
ExpandableListConnector(ExpandableListAdapter* expandableListAdapter);
|
||||
~ExpandableListConnector();
|
||||
void setExpandableListAdapter(ExpandableListAdapter* expandableListAdapter);
|
||||
PositionMetadata* getUnflattenedPos( int flPos)const;/*protected*/
|
||||
PositionMetadata* getFlattenedPos(const ExpandableListPosition& pos);/*proteced*/
|
||||
bool areAllItemsEnabled();
|
||||
bool isEnabled(int flatListPos);
|
||||
int getCount()const;
|
||||
void*getItem(int flatListPos)const;
|
||||
long getItemId(int flatListPos);
|
||||
View* getView(int flatListPos, View* convertView, ViewGroup* parent);
|
||||
int getItemViewType(int flatListPos)const override;
|
||||
int getViewTypeCount()const override;
|
||||
bool hasStableIds()const override;
|
||||
|
||||
bool collapseGroup(int groupPos);
|
||||
bool collapseGroup(PositionMetadata& posMetadata);
|
||||
bool expandGroup(int groupPos);
|
||||
bool expandGroup(PositionMetadata& posMetadata);
|
||||
bool isGroupExpanded(int groupPosition);
|
||||
|
||||
void setMaxExpGroupCount(int maxExpGroupCount);
|
||||
ExpandableListAdapter* getAdapter();
|
||||
Filter* getFilter();
|
||||
|
||||
std::vector<GroupMetadata*>& getExpandedGroupMetadataList();
|
||||
void setExpandedGroupMetadataList(std::vector<GroupMetadata*>& expandedGroupMetadataList);
|
||||
bool isEmpty();
|
||||
int findGroupPosition(long groupIdToMatch, int seedGroupPosition);
|
||||
};
|
||||
|
||||
}/*endof namespace*/
|
||||
#endif
|
@ -1,77 +0,0 @@
|
||||
#include <widget/expandablelistposition.h>
|
||||
#include <widget/expandablelistview.h>
|
||||
namespace cdroid{
|
||||
|
||||
std::queue<ExpandableListPosition*> ExpandableListPosition::sPool;
|
||||
|
||||
void ExpandableListPosition::resetState() {
|
||||
groupPos = 0;
|
||||
childPos = 0;
|
||||
flatListPos = 0;
|
||||
type = 0;
|
||||
}
|
||||
|
||||
ExpandableListPosition::ExpandableListPosition() {
|
||||
}
|
||||
|
||||
long ExpandableListPosition::getPackedPosition() {
|
||||
if (type == CHILD) return ExpandableListView::getPackedPositionForChild(groupPos, childPos);
|
||||
else return ExpandableListView::getPackedPositionForGroup(groupPos);
|
||||
}
|
||||
|
||||
ExpandableListPosition* ExpandableListPosition::obtainGroupPosition(int groupPosition) {
|
||||
return obtain(GROUP, groupPosition, 0, 0);
|
||||
}
|
||||
|
||||
ExpandableListPosition* ExpandableListPosition::obtainChildPosition(int groupPosition, int childPosition) {
|
||||
return obtain(CHILD, groupPosition, childPosition, 0);
|
||||
}
|
||||
|
||||
ExpandableListPosition* ExpandableListPosition::obtainPosition(long packedPosition) {
|
||||
if (packedPosition == ExpandableListView::PACKED_POSITION_VALUE_NULL) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ExpandableListPosition* elp = getRecycledOrCreate();
|
||||
elp->groupPos = ExpandableListView::getPackedPositionGroup(packedPosition);
|
||||
if (ExpandableListView::getPackedPositionType(packedPosition) ==
|
||||
ExpandableListView::PACKED_POSITION_TYPE_CHILD) {
|
||||
elp->type = CHILD;
|
||||
elp->childPos = ExpandableListView::getPackedPositionChild(packedPosition);
|
||||
} else {
|
||||
elp->type = GROUP;
|
||||
}
|
||||
return elp;
|
||||
}
|
||||
|
||||
ExpandableListPosition* ExpandableListPosition::obtain(int type, int groupPos, int childPos, int flatListPos) {
|
||||
ExpandableListPosition* elp = getRecycledOrCreate();
|
||||
elp->type = type;
|
||||
elp->groupPos = groupPos;
|
||||
elp->childPos = childPos;
|
||||
elp->flatListPos = flatListPos;
|
||||
return elp;
|
||||
}
|
||||
|
||||
ExpandableListPosition* ExpandableListPosition::getRecycledOrCreate() {
|
||||
ExpandableListPosition* elp;
|
||||
if (sPool.size() > 0) {
|
||||
elp = sPool.front();
|
||||
sPool.pop();
|
||||
} else {
|
||||
return new ExpandableListPosition();
|
||||
}
|
||||
elp->resetState();
|
||||
return elp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not call this unless you obtained this via ExpandableListPosition.obtain().
|
||||
* PositionMetadata will handle recycling its own children.
|
||||
*/
|
||||
void ExpandableListPosition::recycle()const{
|
||||
if (sPool.size() < MAX_POOL_SIZE) {
|
||||
sPool.push((ExpandableListPosition*)this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
#ifndef __EXPANDABLE_LIST_POSITION_H__
|
||||
#define __EXPANDABLE_LIST_POSITION_H__
|
||||
#include <queue>
|
||||
namespace cdroid{
|
||||
|
||||
class ExpandableListPosition {
|
||||
private:
|
||||
static constexpr int MAX_POOL_SIZE = 5;
|
||||
static std::queue<ExpandableListPosition*> sPool;
|
||||
private:
|
||||
void resetState();
|
||||
ExpandableListPosition();
|
||||
static ExpandableListPosition* getRecycledOrCreate();
|
||||
public:
|
||||
/* This data type represents a child position */
|
||||
static constexpr int CHILD = 1;
|
||||
|
||||
/* This data type represents a group position */
|
||||
static constexpr int GROUP = 2;
|
||||
|
||||
/* The position of either the group being referred to, or the parent
|
||||
* group of the child being referred to */
|
||||
int groupPos;
|
||||
|
||||
/* The position of the child within its parent group */
|
||||
int childPos;
|
||||
|
||||
/* The position of the item in the flat list (optional, used internally when
|
||||
* the corresponding flat list position for the group or child is known) */
|
||||
int flatListPos;
|
||||
|
||||
/* What type of position this ExpandableListPosition represents */
|
||||
int type;
|
||||
public:
|
||||
long getPackedPosition();
|
||||
static ExpandableListPosition* obtainGroupPosition(int groupPosition);
|
||||
static ExpandableListPosition* obtainChildPosition(int groupPosition, int childPosition);
|
||||
static ExpandableListPosition* obtainPosition(long packedPosition);
|
||||
static ExpandableListPosition* obtain(int type, int groupPos, int childPos, int flatListPos);
|
||||
void recycle()const;
|
||||
};
|
||||
|
||||
}/*endof namespace*/
|
||||
#endif/*__EXPANDABLE_LIST_POSITION_H__*/
|
@ -1,614 +0,0 @@
|
||||
#include <widget/expandablelistview.h>
|
||||
namespace cdroid{
|
||||
|
||||
DECLARE_WIDGET(ExpandableListView)
|
||||
|
||||
ExpandableListView::ExpandableListView(int w,int h):ListView(w,h){
|
||||
initView();
|
||||
}
|
||||
|
||||
ExpandableListView::ExpandableListView(Context* context,const AttributeSet& attrs)
|
||||
:ListView(context,attrs){
|
||||
initView();
|
||||
mGroupIndicator = attrs.getDrawable("groupIndicator");
|
||||
mChildIndicator = attrs.getDrawable("childIndicator");
|
||||
mIndicatorLeft = attrs.getDimensionPixelSize("indicatorLeft", 0);
|
||||
mIndicatorRight = attrs.getDimensionPixelSize("indicatorRight", 0);
|
||||
if (mIndicatorRight == 0 && mGroupIndicator ) {
|
||||
mIndicatorRight = mIndicatorLeft + mGroupIndicator->getIntrinsicWidth();
|
||||
}
|
||||
mChildIndicatorLeft = attrs.getDimensionPixelSize("childIndicatorLeft", CHILD_INDICATOR_INHERIT);
|
||||
mChildIndicatorRight = attrs.getDimensionPixelSize("childIndicatorRight", CHILD_INDICATOR_INHERIT);
|
||||
mChildDivider = attrs.getDrawable("childDivider");
|
||||
|
||||
if (!isRtlCompatibilityMode()) {
|
||||
mIndicatorStart = attrs.getDimensionPixelSize("indicatorStart", INDICATOR_UNDEFINED);
|
||||
mIndicatorEnd = attrs.getDimensionPixelSize("indicatorEnd",INDICATOR_UNDEFINED);
|
||||
|
||||
mChildIndicatorStart = attrs.getDimensionPixelSize("childIndicatorStart", CHILD_INDICATOR_INHERIT);
|
||||
mChildIndicatorEnd = attrs.getDimensionPixelSize("childIndicatorEnd", CHILD_INDICATOR_INHERIT);
|
||||
}
|
||||
}
|
||||
|
||||
ExpandableListView::~ExpandableListView(){
|
||||
delete mConnector;
|
||||
delete mGroupIndicator;
|
||||
delete mChildIndicator;
|
||||
delete mChildDivider;
|
||||
}
|
||||
|
||||
void ExpandableListView::initView(){
|
||||
mGroupIndicator = nullptr;
|
||||
mChildIndicator = nullptr;
|
||||
mChildDivider = nullptr;
|
||||
mIndicatorLeft = 0;
|
||||
mIndicatorRight= 0;
|
||||
mConnector = nullptr;
|
||||
mAdapter = nullptr;
|
||||
mChildIndicatorLeft = CHILD_INDICATOR_INHERIT;
|
||||
mChildIndicatorRight= CHILD_INDICATOR_INHERIT;
|
||||
mIndicatorStart = INDICATOR_UNDEFINED;
|
||||
mIndicatorEnd = INDICATOR_UNDEFINED;
|
||||
mChildIndicatorStart = CHILD_INDICATOR_INHERIT;
|
||||
mChildIndicatorEnd = CHILD_INDICATOR_INHERIT;
|
||||
}
|
||||
|
||||
bool ExpandableListView::isRtlCompatibilityMode()const{
|
||||
//final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
|
||||
return /*targetSdkVersion < JELLY_BEAN_MR1 ||*/ !hasRtlSupport();
|
||||
}
|
||||
|
||||
void ExpandableListView::onRtlPropertiesChanged(int layoutDirection){
|
||||
resolveIndicator();
|
||||
resolveChildIndicator();
|
||||
}
|
||||
|
||||
void ExpandableListView::resolveIndicator() {
|
||||
const bool bIsLayoutRtl = isLayoutRtl();
|
||||
if (bIsLayoutRtl) {
|
||||
if (mIndicatorStart >= 0) {
|
||||
mIndicatorRight = mIndicatorStart;
|
||||
}
|
||||
if (mIndicatorEnd >= 0) {
|
||||
mIndicatorLeft = mIndicatorEnd;
|
||||
}
|
||||
} else {
|
||||
if (mIndicatorStart >= 0) {
|
||||
mIndicatorLeft = mIndicatorStart;
|
||||
}
|
||||
if (mIndicatorEnd >= 0) {
|
||||
mIndicatorRight = mIndicatorEnd;
|
||||
}
|
||||
}
|
||||
if (mIndicatorRight == 0 && mGroupIndicator) {
|
||||
mIndicatorRight = mIndicatorLeft + mGroupIndicator->getIntrinsicWidth();
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandableListView::resolveChildIndicator() {
|
||||
const bool bIsLayoutRtl = isLayoutRtl();
|
||||
if (bIsLayoutRtl) {
|
||||
if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
|
||||
mChildIndicatorRight = mChildIndicatorStart;
|
||||
}
|
||||
if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
|
||||
mChildIndicatorLeft = mChildIndicatorEnd;
|
||||
}
|
||||
} else {
|
||||
if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
|
||||
mChildIndicatorLeft = mChildIndicatorStart;
|
||||
}
|
||||
if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
|
||||
mChildIndicatorRight = mChildIndicatorEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandableListView::dispatchDraw(Canvas& canvas) {
|
||||
// Draw children, etc.
|
||||
ListView::dispatchDraw(canvas);
|
||||
|
||||
// If we have any indicators to draw, we do it here
|
||||
if ((mChildIndicator == nullptr) && (mGroupIndicator == nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
|
||||
if (clipToPadding) {
|
||||
canvas.save();
|
||||
canvas.rectangle(mScrollX + mPaddingLeft, mScrollY + mPaddingTop,
|
||||
mScrollX + mRight - mLeft - mPaddingRight,
|
||||
mScrollY + mBottom - mTop - mPaddingBottom);
|
||||
canvas.clip();
|
||||
}
|
||||
|
||||
int headerViewsCount = getHeaderViewsCount();
|
||||
|
||||
int lastChildFlPos = mItemCount - getFooterViewsCount() - headerViewsCount - 1;
|
||||
|
||||
int myB = mBottom;
|
||||
|
||||
ExpandableListConnector::PositionMetadata* pos;
|
||||
View* item;
|
||||
Drawable* indicator;
|
||||
int t, b;
|
||||
|
||||
// Start at a value that is neither child nor group
|
||||
int lastItemType = ~(ExpandableListPosition::CHILD | ExpandableListPosition::GROUP);
|
||||
|
||||
Rect indicatorRect;// = mIndicatorRect;
|
||||
|
||||
// The "child" mentioned in the following two lines is this
|
||||
// View's child, not referring to an expandable list's
|
||||
// notion of a child (as opposed to a group)
|
||||
const int childCount = getChildCount();
|
||||
for (int i = 0, childFlPos = mFirstPosition - headerViewsCount; i < childCount;
|
||||
i++, childFlPos++) {
|
||||
|
||||
if (childFlPos < 0) {
|
||||
// This child is header
|
||||
continue;
|
||||
} else if (childFlPos > lastChildFlPos) {
|
||||
// This child is footer, so are all subsequent children
|
||||
break;
|
||||
}
|
||||
|
||||
item = getChildAt(i);
|
||||
t = item->getTop();
|
||||
b = item->getBottom();
|
||||
|
||||
// This item isn't on the screen
|
||||
if ((b < 0) || (t > myB)) continue;
|
||||
|
||||
// Get more expandable list-related info for this item
|
||||
pos = mConnector->getUnflattenedPos(childFlPos);
|
||||
|
||||
const bool bIsLayoutRtl = isLayoutRtl();
|
||||
const int width = getWidth();
|
||||
|
||||
// If this item type and the previous item type are different, then we need to change
|
||||
// the left & right bounds
|
||||
if (pos->position->type != lastItemType) {
|
||||
if (pos->position->type == ExpandableListPosition::CHILD) {
|
||||
indicatorRect.left = (mChildIndicatorLeft == CHILD_INDICATOR_INHERIT) ?
|
||||
mIndicatorLeft : mChildIndicatorLeft;
|
||||
indicatorRect.width = ((mChildIndicatorRight == CHILD_INDICATOR_INHERIT) ?
|
||||
mIndicatorRight : mChildIndicatorRight) - indicatorRect.left;
|
||||
} else {
|
||||
indicatorRect.left = mIndicatorLeft;
|
||||
indicatorRect.width = mIndicatorRight - mIndicatorLeft;
|
||||
}
|
||||
|
||||
if (bIsLayoutRtl) {
|
||||
const int temp = indicatorRect.left;
|
||||
indicatorRect.left = width - indicatorRect.right();
|
||||
indicatorRect.width = indicatorRect.right() - temp;
|
||||
|
||||
indicatorRect.left -= mPaddingRight;
|
||||
//indicatorRect.right -= mPaddingRight;
|
||||
} else {
|
||||
indicatorRect.left += mPaddingLeft;
|
||||
//indicatorRect.right += mPaddingLeft;
|
||||
}
|
||||
|
||||
lastItemType = pos->position->type;
|
||||
}
|
||||
|
||||
if (indicatorRect.width > 0) {
|
||||
// Use item's full height + the divider height
|
||||
if (mStackFromBottom) {
|
||||
// See ListView#dispatchDraw
|
||||
indicatorRect.top = t;// - mDividerHeight;
|
||||
indicatorRect.height = b - t;
|
||||
} else {
|
||||
indicatorRect.top = t;
|
||||
indicatorRect.height = b - t;// + mDividerHeight;
|
||||
}
|
||||
|
||||
// Get the indicator (with its state set to the item's state)
|
||||
indicator = getIndicator(*pos);
|
||||
if (indicator) {
|
||||
// Draw the indicator
|
||||
indicator->setBounds(indicatorRect);
|
||||
indicator->draw(canvas);
|
||||
}
|
||||
}
|
||||
pos->recycle();
|
||||
}
|
||||
|
||||
if (clipToPadding) {
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
Drawable* ExpandableListView::getIndicator(ExpandableListConnector::PositionMetadata& pos) {
|
||||
Drawable* indicator;
|
||||
|
||||
if (pos.position->type == ExpandableListPosition::GROUP) {
|
||||
indicator = mGroupIndicator;
|
||||
|
||||
if (indicator && indicator->isStateful()) {
|
||||
// Empty check based on availability of data. If the groupMetadata isn't null,
|
||||
// we do a check on it. Otherwise, the group is collapsed so we consider it
|
||||
// empty for performance reasons.
|
||||
const bool isEmpty = (pos.groupMetadata == nullptr) ||
|
||||
(pos.groupMetadata->lastChildFlPos == pos.groupMetadata->flPos);
|
||||
|
||||
const int stateSetIndex = (pos.isExpanded() ? 1 : 0) | // Expanded?
|
||||
(isEmpty ? 2 : 0); // Empty?
|
||||
//indicator->setState(GROUP_STATE_SETS[stateSetIndex]);
|
||||
}
|
||||
} else {
|
||||
indicator = mChildIndicator;
|
||||
|
||||
if (indicator && indicator->isStateful()) {
|
||||
// No need for a state sets array for the child since it only has two states
|
||||
/*int stateSet[] = pos.position.flatListPos == pos.groupMetadata.lastChildFlPos
|
||||
? CHILD_LAST_STATE_SET : EMPTY_STATE_SET;
|
||||
indicator->setState(stateSet);*/
|
||||
}
|
||||
}
|
||||
|
||||
return indicator;
|
||||
}
|
||||
|
||||
void ExpandableListView::setChildDivider(Drawable* childDivider) {
|
||||
mChildDivider = childDivider;
|
||||
}
|
||||
|
||||
void ExpandableListView::drawDivider(Canvas& canvas, Rect bounds, int childIndex) {
|
||||
int flatListPosition = childIndex + mFirstPosition;
|
||||
|
||||
// Only proceed as possible child if the divider isn't above all items (if it is above
|
||||
// all items, then the item below it has to be a group)
|
||||
if (flatListPosition >= 0) {
|
||||
const int adjustedPosition = getFlatPositionForConnector(flatListPosition);
|
||||
ExpandableListConnector::PositionMetadata* pos = mConnector->getUnflattenedPos(adjustedPosition);
|
||||
// If this item is a child, or it is a non-empty group that is expanded
|
||||
if ((pos->position->type == ExpandableListPosition::CHILD) || (pos->isExpanded() &&
|
||||
pos->groupMetadata->lastChildFlPos != pos->groupMetadata->flPos)) {
|
||||
// These are the cases where we draw the child divider
|
||||
Drawable* divider = mChildDivider;
|
||||
divider->setBounds(bounds);
|
||||
divider->draw(canvas);
|
||||
pos->recycle();
|
||||
return;
|
||||
}
|
||||
pos->recycle();
|
||||
}
|
||||
|
||||
// Otherwise draw the default divider
|
||||
ListView::drawDivider(canvas, bounds, flatListPosition);
|
||||
}
|
||||
|
||||
/*void ExpandableListView::setOnItemClickListener(OnItemClickListener l) {
|
||||
ListView::setOnItemClickListener(l);
|
||||
}*/
|
||||
void ExpandableListView::setAdapter(ExpandableListAdapter* adapter){
|
||||
mAdapter = adapter;
|
||||
if (adapter != nullptr) {
|
||||
// Create the connector
|
||||
mConnector = new ExpandableListConnector(adapter);
|
||||
} else {
|
||||
mConnector = nullptr;
|
||||
}
|
||||
|
||||
// Link the ListView (superclass) to the expandable list data through the connector
|
||||
ListView::setAdapter(mConnector);
|
||||
}
|
||||
|
||||
ExpandableListAdapter* ExpandableListView::getExpandableListAdapter(){
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
void ExpandableListView::setAdapter(ListAdapter* adapter) {
|
||||
FATAL("For ExpandableListView, use setAdapter(ExpandableListAdapter) instead of setAdapter(ListAdapter)");
|
||||
}
|
||||
|
||||
bool ExpandableListView::isHeaderOrFooterPosition(int position) {
|
||||
const int footerViewsStart = mItemCount - getFooterViewsCount();
|
||||
return (position < getHeaderViewsCount() || position >= footerViewsStart);
|
||||
}
|
||||
|
||||
int ExpandableListView::getFlatPositionForConnector(int flatListPosition) {
|
||||
return flatListPosition - getHeaderViewsCount();
|
||||
}
|
||||
|
||||
|
||||
int ExpandableListView::getAbsoluteFlatPosition(int flatListPosition) {
|
||||
return flatListPosition + getHeaderViewsCount();
|
||||
}
|
||||
|
||||
bool ExpandableListView::performItemClick(View& v, int position, long id) {
|
||||
// Ignore clicks in header/footers
|
||||
if (isHeaderOrFooterPosition(position)) {
|
||||
// Clicked on a header/footer, so ignore pass it on to super
|
||||
return ListView::performItemClick(v, position, id);
|
||||
}
|
||||
|
||||
// Internally handle the item click
|
||||
const int adjustedPosition = getFlatPositionForConnector(position);
|
||||
return handleItemClick(v, adjustedPosition, id);
|
||||
}
|
||||
|
||||
bool ExpandableListView::handleItemClick(View& v, int position, long id) {
|
||||
ExpandableListConnector::PositionMetadata* posMetadata = mConnector->getUnflattenedPos(position);
|
||||
|
||||
id = getChildOrGroupId(*posMetadata->position);
|
||||
|
||||
bool returnValue;
|
||||
if (posMetadata->position->type == ExpandableListPosition::GROUP) {
|
||||
/* It's a group, so handle collapsing/expanding */
|
||||
|
||||
/* It's a group click, so pass on event */
|
||||
if (mOnGroupClickListener) {
|
||||
if (mOnGroupClickListener(*this, v, posMetadata->position->groupPos, id)) {
|
||||
posMetadata->recycle();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (posMetadata->isExpanded()) {
|
||||
/* Collapse it */
|
||||
mConnector->collapseGroup(*posMetadata);
|
||||
|
||||
playSoundEffect(SoundEffectConstants::CLICK);
|
||||
|
||||
if (mOnGroupCollapseListener) {
|
||||
mOnGroupCollapseListener(posMetadata->position->groupPos);
|
||||
}
|
||||
} else {
|
||||
/* Expand it */
|
||||
mConnector->expandGroup(*posMetadata);
|
||||
|
||||
playSoundEffect(SoundEffectConstants::CLICK);
|
||||
|
||||
if (mOnGroupExpandListener) {
|
||||
mOnGroupExpandListener(posMetadata->position->groupPos);
|
||||
}
|
||||
|
||||
const int groupPos = posMetadata->position->groupPos;
|
||||
const int groupFlatPos = posMetadata->position->flatListPos;
|
||||
|
||||
const int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
|
||||
smoothScrollToPosition(shiftedGroupPosition + mAdapter->getChildrenCount(groupPos),
|
||||
shiftedGroupPosition);
|
||||
}
|
||||
|
||||
returnValue = true;
|
||||
} else {
|
||||
/* It's a child, so pass on event */
|
||||
if (mOnChildClickListener) {
|
||||
playSoundEffect(SoundEffectConstants::CLICK);
|
||||
return mOnChildClickListener(*this, v, posMetadata->position->groupPos,
|
||||
posMetadata->position->childPos, id);
|
||||
}
|
||||
|
||||
returnValue = false;
|
||||
}
|
||||
|
||||
posMetadata->recycle();
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
bool ExpandableListView::expandGroup(int groupPos) {
|
||||
return expandGroup(groupPos, false);
|
||||
}
|
||||
|
||||
bool ExpandableListView::expandGroup(int groupPos, bool animate) {
|
||||
ExpandableListPosition* elGroupPos = ExpandableListPosition::obtain(
|
||||
ExpandableListPosition::GROUP, groupPos, -1, -1);
|
||||
ExpandableListConnector::PositionMetadata* pm = mConnector->getFlattenedPos(*elGroupPos);
|
||||
elGroupPos->recycle();
|
||||
const bool retValue = mConnector->expandGroup(*pm);
|
||||
|
||||
if (mOnGroupExpandListener) {
|
||||
mOnGroupExpandListener(groupPos);
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
const int groupFlatPos = pm->position->flatListPos;
|
||||
|
||||
const int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
|
||||
smoothScrollToPosition(shiftedGroupPosition + mAdapter->getChildrenCount(groupPos),
|
||||
shiftedGroupPosition);
|
||||
}
|
||||
pm->recycle();
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
bool ExpandableListView::collapseGroup(int groupPos) {
|
||||
const bool retValue = mConnector->collapseGroup(groupPos);
|
||||
|
||||
if (mOnGroupCollapseListener ) {
|
||||
mOnGroupCollapseListener(groupPos);
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
void ExpandableListView::setOnGroupCollapseListener(OnGroupCollapseListener onGroupCollapseListener){
|
||||
mOnGroupCollapseListener = onGroupCollapseListener;
|
||||
}
|
||||
|
||||
void ExpandableListView::setOnGroupExpandListener( OnGroupExpandListener onGroupExpandListener) {
|
||||
mOnGroupExpandListener = onGroupExpandListener;
|
||||
}
|
||||
|
||||
void ExpandableListView::setOnGroupClickListener(OnGroupClickListener onGroupClickListener) {
|
||||
mOnGroupClickListener = onGroupClickListener;
|
||||
}
|
||||
|
||||
void ExpandableListView::setOnChildClickListener(OnChildClickListener onChildClickListener) {
|
||||
mOnChildClickListener = onChildClickListener;
|
||||
}
|
||||
|
||||
long ExpandableListView::getExpandableListPosition(int flatListPosition) {
|
||||
if (isHeaderOrFooterPosition(flatListPosition)) {
|
||||
return PACKED_POSITION_VALUE_NULL;
|
||||
}
|
||||
const int adjustedPosition = getFlatPositionForConnector(flatListPosition);
|
||||
ExpandableListConnector::PositionMetadata* pm = mConnector->getUnflattenedPos(adjustedPosition);
|
||||
const long packedPos = pm->position->getPackedPosition();
|
||||
pm->recycle();
|
||||
return packedPos;
|
||||
}
|
||||
|
||||
int ExpandableListView::getFlatListPosition(long packedPosition) {
|
||||
ExpandableListPosition* elPackedPos = ExpandableListPosition::obtainPosition(packedPosition);
|
||||
ExpandableListConnector::PositionMetadata* pm = mConnector->getFlattenedPos(*elPackedPos);
|
||||
elPackedPos->recycle();
|
||||
const int flatListPosition = pm->position->flatListPos;
|
||||
pm->recycle();
|
||||
return getAbsoluteFlatPosition(flatListPosition);
|
||||
}
|
||||
|
||||
long ExpandableListView::getSelectedPosition() {
|
||||
const int selectedPos = getSelectedItemPosition();
|
||||
|
||||
// The case where there is no selection (selectedPos == -1) is also handled here.
|
||||
return getExpandableListPosition(selectedPos);
|
||||
}
|
||||
|
||||
long ExpandableListView::getSelectedId() {
|
||||
long packedPos = getSelectedPosition();
|
||||
if (packedPos == PACKED_POSITION_VALUE_NULL) return -1;
|
||||
|
||||
int groupPos = getPackedPositionGroup(packedPos);
|
||||
|
||||
if (getPackedPositionType(packedPos) == PACKED_POSITION_TYPE_GROUP) {
|
||||
// It's a group
|
||||
return mAdapter->getGroupId(groupPos);
|
||||
} else {
|
||||
// It's a child
|
||||
return mAdapter->getChildId(groupPos, getPackedPositionChild(packedPos));
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandableListView::setSelectedGroup(int groupPosition) {
|
||||
ExpandableListPosition* elGroupPos = ExpandableListPosition::obtainGroupPosition(groupPosition);
|
||||
ExpandableListConnector::PositionMetadata* pm = mConnector->getFlattenedPos(*elGroupPos);
|
||||
elGroupPos->recycle();
|
||||
const int absoluteFlatPosition = getAbsoluteFlatPosition(pm->position->flatListPos);
|
||||
ListView::setSelection(absoluteFlatPosition);
|
||||
pm->recycle();
|
||||
}
|
||||
|
||||
bool ExpandableListView::setSelectedChild(int groupPosition, int childPosition, bool shouldExpandGroup) {
|
||||
ExpandableListPosition* elChildPos = ExpandableListPosition::obtainChildPosition(groupPosition, childPosition);
|
||||
ExpandableListConnector::PositionMetadata* flatChildPos = mConnector->getFlattenedPos(*elChildPos);
|
||||
|
||||
if (flatChildPos == nullptr) {
|
||||
// The child's group isn't expanded
|
||||
|
||||
// Shouldn't expand the group, so return false for we didn't set the selection
|
||||
if (!shouldExpandGroup) return false;
|
||||
|
||||
expandGroup(groupPosition);
|
||||
|
||||
flatChildPos = mConnector->getFlattenedPos(*elChildPos);
|
||||
|
||||
// Validity check
|
||||
if (flatChildPos == nullptr) {
|
||||
FATAL("Could not find child");
|
||||
}
|
||||
}
|
||||
|
||||
const int absoluteFlatPosition = getAbsoluteFlatPosition(flatChildPos->position->flatListPos);
|
||||
ListView::setSelection(absoluteFlatPosition);
|
||||
|
||||
elChildPos->recycle();
|
||||
flatChildPos->recycle();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExpandableListView::isGroupExpanded(int groupPosition) {
|
||||
return mConnector->isGroupExpanded(groupPosition);
|
||||
}
|
||||
|
||||
int ExpandableListView::getPackedPositionType(long packedPosition) {
|
||||
if (packedPosition == PACKED_POSITION_VALUE_NULL) {
|
||||
return PACKED_POSITION_TYPE_NULL;
|
||||
}
|
||||
return (packedPosition & PACKED_POSITION_MASK_TYPE) == PACKED_POSITION_MASK_TYPE
|
||||
? PACKED_POSITION_TYPE_CHILD : PACKED_POSITION_TYPE_GROUP;
|
||||
}
|
||||
|
||||
int ExpandableListView::getPackedPositionGroup(long packedPosition) {
|
||||
// Null
|
||||
if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;
|
||||
|
||||
return (int) ((packedPosition & PACKED_POSITION_MASK_GROUP) >> PACKED_POSITION_SHIFT_GROUP);
|
||||
}
|
||||
|
||||
int ExpandableListView::getPackedPositionChild(long packedPosition) {
|
||||
// Null
|
||||
if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;
|
||||
|
||||
// Group since a group type clears this bit
|
||||
if ((packedPosition & PACKED_POSITION_MASK_TYPE) != PACKED_POSITION_MASK_TYPE) return -1;
|
||||
|
||||
return (int) (packedPosition & PACKED_POSITION_MASK_CHILD);
|
||||
}
|
||||
|
||||
long ExpandableListView::getPackedPositionForChild(int groupPosition, int childPosition) {
|
||||
return (((int64_t)PACKED_POSITION_TYPE_CHILD) << PACKED_POSITION_SHIFT_TYPE)
|
||||
| ((((int64_t)groupPosition) & PACKED_POSITION_INT_MASK_GROUP)
|
||||
<< PACKED_POSITION_SHIFT_GROUP)
|
||||
| (childPosition & PACKED_POSITION_INT_MASK_CHILD);
|
||||
}
|
||||
|
||||
long ExpandableListView::getPackedPositionForGroup(int groupPosition) {
|
||||
// No need to OR a type in because PACKED_POSITION_GROUP == 0
|
||||
return ((((int64_t)groupPosition) & PACKED_POSITION_INT_MASK_GROUP)
|
||||
<< PACKED_POSITION_SHIFT_GROUP);
|
||||
}
|
||||
|
||||
long ExpandableListView::getChildOrGroupId(const ExpandableListPosition& position) {
|
||||
if (position.type == ExpandableListPosition::CHILD) {
|
||||
return mAdapter->getChildId(position.groupPos, position.childPos);
|
||||
} else {
|
||||
return mAdapter->getGroupId(position.groupPos);
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandableListView::setChildIndicator(Drawable* childIndicator) {
|
||||
mChildIndicator = childIndicator;
|
||||
}
|
||||
|
||||
void ExpandableListView::setChildIndicatorBounds(int left, int right) {
|
||||
mChildIndicatorLeft = left;
|
||||
mChildIndicatorRight = right;
|
||||
resolveChildIndicator();
|
||||
}
|
||||
|
||||
void ExpandableListView::setChildIndicatorBoundsRelative(int start, int end) {
|
||||
mChildIndicatorStart = start;
|
||||
mChildIndicatorEnd = end;
|
||||
resolveChildIndicator();
|
||||
}
|
||||
|
||||
void ExpandableListView::setGroupIndicator(Drawable* groupIndicator) {
|
||||
mGroupIndicator = groupIndicator;
|
||||
if (mIndicatorRight == 0 && mGroupIndicator ) {
|
||||
mIndicatorRight = mIndicatorLeft + mGroupIndicator->getIntrinsicWidth();
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandableListView::setIndicatorBounds(int left, int right) {
|
||||
mIndicatorLeft = left;
|
||||
mIndicatorRight = right;
|
||||
resolveIndicator();
|
||||
}
|
||||
|
||||
void ExpandableListView::setIndicatorBoundsRelative(int start, int end) {
|
||||
mIndicatorStart = start;
|
||||
mIndicatorEnd = end;
|
||||
resolveIndicator();
|
||||
}
|
||||
|
||||
}/*endof namespace*/
|
||||
|
@ -1,148 +0,0 @@
|
||||
#ifndef __EXPANDABLE_LISTVIEW_H__
|
||||
#define __EXPANDABLE_LISTVIEW_H__
|
||||
#include <widget/listview.h>
|
||||
#include <widget/expandablelistconnector.h>
|
||||
#include <widget/expandablelistposition.h>
|
||||
#include <widget/baseexpandablelistadapter.h>
|
||||
|
||||
namespace cdroid{
|
||||
|
||||
class ExpandableListView:public ListView{
|
||||
private:
|
||||
static constexpr int64_t PACKED_POSITION_MASK_CHILD = 0x00000000FFFFFFFFL;
|
||||
|
||||
/** The mask (in packed position representation) for the group */
|
||||
static constexpr int64_t PACKED_POSITION_MASK_GROUP = 0x7FFFFFFF00000000L;
|
||||
|
||||
/** The mask (in packed position representation) for the type */
|
||||
static constexpr int64_t PACKED_POSITION_MASK_TYPE = 0x8000000000000000L;
|
||||
|
||||
/** The shift amount (in packed position representation) for the group */
|
||||
static constexpr long PACKED_POSITION_SHIFT_GROUP = 32;
|
||||
|
||||
/** The shift amount (in packed position representation) for the type */
|
||||
static constexpr long PACKED_POSITION_SHIFT_TYPE = 63;
|
||||
|
||||
/** The mask (in integer child position representation) for the child */
|
||||
static constexpr long PACKED_POSITION_INT_MASK_CHILD = 0xFFFFFFFF;
|
||||
|
||||
/** The mask (in integer group position representation) for the group */
|
||||
static constexpr long PACKED_POSITION_INT_MASK_GROUP = 0x7FFFFFFF;
|
||||
/* Denotes an undefined value for an indicator */
|
||||
static constexpr int INDICATOR_UNDEFINED = -2;
|
||||
public:
|
||||
/* The packed position represents a group. */
|
||||
static constexpr int PACKED_POSITION_TYPE_GROUP = 0;
|
||||
|
||||
/* The packed position represents a child. */
|
||||
static constexpr int PACKED_POSITION_TYPE_CHILD = 1;
|
||||
|
||||
/* The packed position represents a neither/null/no preference. */
|
||||
static constexpr int PACKED_POSITION_TYPE_NULL = 2;
|
||||
|
||||
/* The value for a packed position that represents neither/null/no
|
||||
* preference. This value is not otherwise possible since a group type
|
||||
* (first bit 0) should not have a child position filled. */
|
||||
static constexpr long PACKED_POSITION_VALUE_NULL = 0x00000000FFFFFFFFL;
|
||||
/* Denotes when a child indicator should inherit this bound from the generic
|
||||
* indicator bounds */
|
||||
static constexpr int CHILD_INDICATOR_INHERIT = -1;
|
||||
DECLARE_UIEVENT(void,OnGroupExpandListener,int);
|
||||
DECLARE_UIEVENT(void,OnGroupCollapseListener,int);
|
||||
DECLARE_UIEVENT(bool,OnGroupClickListener,ExpandableListView& parent, View& v, int groupPosition, long id);
|
||||
DECLARE_UIEVENT(bool,OnChildClickListener,ExpandableListView& parent, View& v, int groupPosition, int childPosition, long id);
|
||||
private:
|
||||
int mIndicatorLeft;
|
||||
|
||||
/** Right bound for drawing the indicator. */
|
||||
int mIndicatorRight;
|
||||
|
||||
/** Start bound for drawing the indicator. */
|
||||
int mIndicatorStart;
|
||||
|
||||
/** End bound for drawing the indicator. */
|
||||
int mIndicatorEnd;
|
||||
|
||||
/* Left bound for drawing the indicator of a child. Value of
|
||||
* {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorLeft.*/
|
||||
int mChildIndicatorLeft;
|
||||
|
||||
/* Right bound for drawing the indicator of a child. Value of
|
||||
* {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorRight. */
|
||||
int mChildIndicatorRight;
|
||||
|
||||
/* Start bound for drawing the indicator of a child. Value of
|
||||
* {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorStart.*/
|
||||
int mChildIndicatorStart;
|
||||
|
||||
/* End bound for drawing the indicator of a child. Value of
|
||||
* {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorEnd. */
|
||||
int mChildIndicatorEnd;
|
||||
|
||||
ExpandableListConnector* mConnector;
|
||||
ExpandableListAdapter* mAdapter;
|
||||
/** The indicator drawn next to a group. */
|
||||
Drawable* mGroupIndicator;
|
||||
|
||||
/** The indicator drawn next to a child. */
|
||||
Drawable* mChildIndicator;
|
||||
/** Drawable to be used as a divider when it is adjacent to any children */
|
||||
Drawable* mChildDivider;
|
||||
OnGroupExpandListener mOnGroupExpandListener;
|
||||
OnGroupCollapseListener mOnGroupCollapseListener;
|
||||
OnGroupClickListener mOnGroupClickListener;
|
||||
OnChildClickListener mOnChildClickListener;
|
||||
private:
|
||||
void initView();
|
||||
bool isRtlCompatibilityMode()const;
|
||||
void resolveIndicator();
|
||||
void resolveChildIndicator();
|
||||
Drawable* getIndicator(ExpandableListConnector::PositionMetadata& pos);
|
||||
bool isHeaderOrFooterPosition(int position);
|
||||
int getFlatPositionForConnector(int flatListPosition);
|
||||
int getAbsoluteFlatPosition(int flatListPosition);
|
||||
long getChildOrGroupId(const ExpandableListPosition& position);
|
||||
protected:
|
||||
void dispatchDraw(Canvas& canvas);
|
||||
void drawDivider(Canvas& canvas, Rect bounds, int childIndex);
|
||||
bool handleItemClick(View& v, int position, long id);
|
||||
public:
|
||||
ExpandableListView(int,int);
|
||||
ExpandableListView(Context* context,const AttributeSet& attrs);
|
||||
~ExpandableListView();
|
||||
void setAdapter(ListAdapter* adapter);
|
||||
void setAdapter(ExpandableListAdapter* adapter);
|
||||
ExpandableListAdapter* getExpandableListAdapter();
|
||||
void onRtlPropertiesChanged(int layoutDirection);
|
||||
void setChildDivider(Drawable* childDivider);
|
||||
bool performItemClick(View& v, int position, long id)override;
|
||||
bool expandGroup(int groupPos);
|
||||
bool expandGroup(int groupPos, bool animate);
|
||||
bool collapseGroup(int groupPos);
|
||||
void setOnGroupCollapseListener(OnGroupCollapseListener onGroupCollapseListener);
|
||||
void setOnGroupExpandListener( OnGroupExpandListener onGroupExpandListener);
|
||||
void setOnGroupClickListener(OnGroupClickListener onGroupClickListener);
|
||||
void setOnChildClickListener(OnChildClickListener onChildClickListener);
|
||||
long getExpandableListPosition(int flatListPosition);
|
||||
int getFlatListPosition(long packedPosition);
|
||||
long getSelectedPosition();
|
||||
long getSelectedId();
|
||||
void setSelectedGroup(int groupPosition);
|
||||
bool setSelectedChild(int groupPosition, int childPosition, bool shouldExpandGroup);
|
||||
bool isGroupExpanded(int groupPosition);
|
||||
|
||||
static int getPackedPositionType(long packedPosition);
|
||||
static int getPackedPositionGroup(long packedPosition);
|
||||
static int getPackedPositionChild(long packedPosition);
|
||||
static long getPackedPositionForChild(int groupPosition, int childPosition);
|
||||
static long getPackedPositionForGroup(int groupPosition);
|
||||
|
||||
void setChildIndicator(Drawable* childIndicator);
|
||||
void setChildIndicatorBounds(int left, int right);
|
||||
void setChildIndicatorBoundsRelative(int start, int end);
|
||||
void setGroupIndicator(Drawable* groupIndicator);
|
||||
void setIndicatorBounds(int left, int right);
|
||||
void setIndicatorBoundsRelative(int start, int end);
|
||||
};
|
||||
}/*endof namespace*/
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user