add dropdownlistview

This commit is contained in:
侯歌 2021-12-02 17:31:14 +08:00
parent 7a6ca7af18
commit 4b05691c3d
11 changed files with 907 additions and 41 deletions

View File

@ -1,18 +1,13 @@
#include<theme.h>
#include<expat.h>
#include<cdtypes.h>
#include<cdlog.h>
#include<iostream>
#include <theme.h>
#include <expat.h>
#include <cdtypes.h>
#include <cdlog.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string.h>
namespace cdroid{
static void startElement(void *userData, const XML_Char *name, const XML_Char **atts){
}
static void endElement(void *userData, const XML_Char *name){
}
Theme*Theme::mInst=nullptr;
Theme&Theme::getInstance(){
@ -21,29 +16,79 @@ Theme&Theme::getInstance(){
return *mInst;
}
int Theme::parseStyles(std::istream&stream){
int done=0;
typedef struct StyleData{
std::map<const std::string,AttributeSet>*maps;
AttributeSet*cur;
std::string key;
std::string value;
}STYLEDATA;
static void startElement(void *userData, const XML_Char *name, const XML_Char **satts){
AttributeSet atts(satts);
STYLEDATA*sd=(STYLEDATA*)userData;
std::map<const std::string,AttributeSet>*maps=sd->maps;
if(0==strcmp(name,"style")){
const std::string stylename=atts.getString("name");
const std::string parent=atts.getString("parent");
auto it=maps->find(stylename);
LOGD("<%s> parent=%s",stylename.c_str(),parent.c_str());
if(it!=maps->end()){
sd->cur=&it->second;
}else{
auto it2=maps->insert(std::make_pair<const std::string,AttributeSet>(stylename.c_str(),AttributeSet()));
sd->cur=&(it2.first->second);
if(!parent.empty())sd->cur->add("parent",parent);
}
}else if(0==strcmp(name,"item")){
sd->key = atts.getString("name");
sd->value=std::string();
}
}
static void CharacterHandler(void *userData,const XML_Char *s, int len){
STYLEDATA*sd=(STYLEDATA*)userData;
sd->value+=std::string(s,len);
}
static void endElement(void *userData, const XML_Char *name){
STYLEDATA*sd=(STYLEDATA*)userData;
std::map<const std::string,AttributeSet>*maps=sd->maps;
if(0==strcmp(name,"style")){
sd->cur=nullptr;
}else if(0==strcmp(name,"item")){
LOGV("\t%s=%s",sd->key.c_str(),sd->value.c_str());
sd->cur->add(sd->key,sd->value);
}
}
int Theme::loadStyles(std::istream&stream){
int len;
char buf[256];
XML_Parser parser = XML_ParserCreate(NULL);
XML_SetUserData(parser,nullptr);
XML_Parser parser=XML_ParserCreate(nullptr);
std::string curKey;
std::string curValue;
STYLEDATA sd={&mStyles,nullptr};
XML_SetUserData(parser,&sd);
XML_SetElementHandler(parser, startElement, endElement);
//XML_SetCharacterDataHandler(parser,dataHandler);
XML_SetCharacterDataHandler(parser,CharacterHandler);
do {
int len=stream.readsome(buf,sizeof(buf));
done=(len==0);
if (XML_Parse(parser, buf,len,done) == XML_STATUS_ERROR) {
const char*es=XML_ErrorString(XML_GetErrorCode(parser));
LOGE("%s at line %ld",es, XML_GetCurrentLineNumber(parser));
return 1;
}
} while(!done);
stream.read(buf,sizeof(buf));
len=stream.gcount();
if (XML_Parse(parser, buf,len,len==0) == XML_STATUS_ERROR) {
const char*es=XML_ErrorString(XML_GetErrorCode(parser));
LOGE("%s at line %ld",es, XML_GetCurrentLineNumber(parser));
XML_ParserFree(parser);
return 0;
}
} while(len!=0);
XML_ParserFree(parser);
return 0;
}
const AttributeSet Theme::getStyle(const std::string&name)const{
auto it=mStyles.find(name);
if(it!=mStyles.end())
return it->second;
return AttributeSet();
}

View File

@ -11,11 +11,11 @@ namespace cdroid{
class Theme{
protected:
std::map<std::string,AttributeSet>mStyles;
std::map<const std::string,AttributeSet>mStyles;
static Theme*mInst;
public:
static Theme&getInstance();
int parseStyles(std::istream&s);
int loadStyles(std::istream&s);
const AttributeSet getStyle(const std::string&name)const;
};

View File

@ -1649,6 +1649,26 @@ bool AbsListView::canScrollDown() {
return canScrollDown;
}
void AbsListView::scrollListBy(int y){
trackMotionScroll(-y,-y);
}
bool AbsListView::canScrollList(int direction){
const int childCount = getChildCount();
if (childCount == 0) return false;
int firstPosition = mFirstPosition;
Rect listPadding = mListPadding;
if (direction > 0) {
const int lastBottom = getChildAt(childCount - 1)->getBottom();
const int lastPosition = firstPosition + childCount;
return lastPosition < mItemCount || lastBottom > getHeight() - listPadding.height;//bottom;
} else {
const int firstTop = getChildAt(0)->getTop();
return firstPosition > 0 || firstTop < listPadding.top;
}
}
bool AbsListView::trackMotionScroll(int deltaY, int incrementalDeltaY) {
int childCount = getChildCount();
if (childCount == 0) {

View File

@ -241,8 +241,8 @@ protected:
void onMeasure(int widthMeasureSpec, int heightMeasureSpec)override;
void updateScrollIndicators();
void setScrollIndicatorViews(View* up, View* down);
bool touchModeDrawsInPressedState();
bool shouldShowSelector();
virtual bool touchModeDrawsInPressedState();
virtual bool shouldShowSelector();
void updateSelectorState();
void drawableStateChanged()override;
virtual void layoutChildren();
@ -253,7 +253,7 @@ protected:
void confirmCheckedPositionsById();
void handleDataChanged()override;
static int getDistance(const Rect& source,const Rect& dest, int direction);
View* obtainView(int position, bool*outMetadata);
virtual View* obtainView(int position, bool*outMetadata);
void positionSelector(int position, View* sel);
void hideSelector();
void reportScrollStateChange(int newState);
@ -351,6 +351,8 @@ public:
void smoothScrollBy(int distance, int duration);
void smoothScrollBy(int distance, int duration, bool linear,bool suppressEndFlingStateChangeCall);
void smoothScrollByOffset(int position);
void scrollListBy(int y);
bool canScrollList(int direction);
void reclaimViews(std::vector<View*>& views);
};

View File

@ -0,0 +1,430 @@
#include <widget/autoscrollhelper.h>
#include <widget/abslistview.h>
namespace cdroid{
AutoScrollHelper::AutoScrollHelper(View* target) {
mTarget = target;
//DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
//int maxVelocity = (int) (DEFAULT_MAXIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
//int minVelocity = (int) (DEFAULT_MINIMUM_VELOCITY_DIPS * metrics.density + 0.5f);
int maxVelocity = DEFAULT_MAXIMUM_VELOCITY_DIPS*160;
int minVelocity = DEFAULT_MINIMUM_VELOCITY_DIPS*160;
setMaximumVelocity(maxVelocity, maxVelocity);
setMinimumVelocity(minVelocity, minVelocity);
setEdgeType(DEFAULT_EDGE_TYPE);
setMaximumEdges(DEFAULT_MAXIMUM_EDGE, DEFAULT_MAXIMUM_EDGE);
setRelativeEdges(DEFAULT_RELATIVE_EDGE, DEFAULT_RELATIVE_EDGE);
setRelativeVelocity(DEFAULT_RELATIVE_VELOCITY, DEFAULT_RELATIVE_VELOCITY);
setActivationDelay(DEFAULT_ACTIVATION_DELAY);
setRampUpDuration(DEFAULT_RAMP_UP_DURATION);
setRampDownDuration(DEFAULT_RAMP_DOWN_DURATION);
mScroller =new ClampedScroller();
mEdgeInterpolator = new AccelerateInterpolator();
}
AutoScrollHelper::~AutoScrollHelper(){
delete mScroller;
delete mEdgeInterpolator;
}
AutoScrollHelper& AutoScrollHelper::setEnabled(bool enabled) {
if (mEnabled && !enabled) {
requestStop();
}
mEnabled = enabled;
return *this;
}
bool AutoScrollHelper::isEnabled()const{
return mEnabled;
}
AutoScrollHelper& AutoScrollHelper::setExclusive(bool exclusive) {
mExclusive = exclusive;
return *this;
}
bool AutoScrollHelper::isExclusive()const{
return mExclusive;
}
AutoScrollHelper& AutoScrollHelper::setMaximumVelocity(float horizontalMax, float verticalMax) {
mMaximumVelocity[HORIZONTAL] = horizontalMax / 1000.f;
mMaximumVelocity[VERTICAL] = verticalMax / 1000.f;
return *this;
}
AutoScrollHelper& AutoScrollHelper::setMinimumVelocity(float horizontalMin, float verticalMin) {
mMinimumVelocity[HORIZONTAL] = horizontalMin / 1000.f;
mMinimumVelocity[VERTICAL] = verticalMin / 1000.f;
return *this;
}
AutoScrollHelper& AutoScrollHelper::setRelativeVelocity(float horizontal, float vertical){
mRelativeVelocity[HORIZONTAL] = horizontal / 1000.f;
mRelativeVelocity[VERTICAL] = vertical / 1000.f;
return*this;
}
AutoScrollHelper& AutoScrollHelper::setEdgeType(int type){
mEdgeType =type;
return *this;
}
AutoScrollHelper& AutoScrollHelper::setRelativeEdges(float horizontal, float vertical) {
mRelativeEdges[HORIZONTAL] = horizontal;
mRelativeEdges[VERTICAL] = vertical;
return *this;
}
AutoScrollHelper& AutoScrollHelper::setMaximumEdges(float horizontalMax, float verticalMax) {
mMaximumEdges[HORIZONTAL] = horizontalMax;
mMaximumEdges[VERTICAL] = verticalMax;
return *this;
}
AutoScrollHelper& AutoScrollHelper::setActivationDelay(int delayMillis) {
mActivationDelay = delayMillis;
return *this;
}
AutoScrollHelper& AutoScrollHelper::setRampUpDuration(int durationMillis) {
mScroller->setRampUpDuration(durationMillis);
return *this;
}
AutoScrollHelper& AutoScrollHelper::setRampDownDuration(int durationMillis) {
mScroller->setRampDownDuration(durationMillis);
return *this;
}
bool AutoScrollHelper::onTouch(View& v, MotionEvent& event) {
if (!mEnabled) {
return false;
}
float xTargetVelocity=.0,yTargetVelocity=.0;
int action = event.getActionMasked();
switch (action) {
case MotionEvent::ACTION_DOWN:
mNeedsCancel = true;
mAlreadyDelayed = false;
// $FALL-THROUGH$
case MotionEvent::ACTION_MOVE:
xTargetVelocity = computeTargetVelocity(
HORIZONTAL, event.getX(), v.getWidth(), mTarget->getWidth());
yTargetVelocity = computeTargetVelocity(
VERTICAL, event.getY(), v.getHeight(), mTarget->getHeight());
mScroller->setTargetVelocity(xTargetVelocity, yTargetVelocity);
// If the auto scroller was not previously active, but it should
// be, then update the state and start animations.
if (!mAnimating && shouldAnimate()) {
startAnimating();
}
break;
case MotionEvent::ACTION_UP:
case MotionEvent::ACTION_CANCEL:
requestStop();
break;
}
return mExclusive && mAnimating;
}
bool AutoScrollHelper::shouldAnimate() {
const int verticalDirection = mScroller->getVerticalDirection();
const int horizontalDirection = mScroller->getHorizontalDirection();
return verticalDirection != 0 && canTargetScrollVertically(verticalDirection)
|| horizontalDirection != 0 && canTargetScrollHorizontally(horizontalDirection);
}
void AutoScrollHelper::startAnimating() {
if (mRunnable == nullptr) {
mRunnable = std::bind(&AutoScrollHelper::animationRun,this);
}
mAnimating = true;
mNeedsReset = true;
if (!mAlreadyDelayed && mActivationDelay > 0) {
mTarget->postOnAnimationDelayed(mRunnable, mActivationDelay);
} else {
mRunnable();
}
// If we start animating again before the user lifts their finger, we
// already know it's not a tap and don't need an activation delay.
mAlreadyDelayed = true;
}
void AutoScrollHelper::requestStop() {
if (mNeedsReset) {
// The animation has been posted, but hasn't run yet. Manually
// stopping animation will prevent it from running.
mAnimating = false;
} else {
mScroller->requestStop();
}
}
template<typename T>
T constrain(T value, T min, T max) {
if (value > max) {
return max;
} else if (value < min) {
return min;
} else {
return value;
}
}
float AutoScrollHelper::computeTargetVelocity(int direction, float coordinate, float srcSize, float dstSize) {
float relativeEdge = mRelativeEdges[direction];
float maximumEdge = mMaximumEdges[direction];
float value = getEdgeValue(relativeEdge, srcSize, maximumEdge, coordinate);
if (value == 0) {
// The edge in this direction is not activated.
return 0;
}
float relativeVelocity = mRelativeVelocity[direction];
float minimumVelocity = mMinimumVelocity[direction];
float maximumVelocity = mMaximumVelocity[direction];
float targetVelocity = relativeVelocity * dstSize;
// Target velocity is adjusted for interpolated edge position, then
// clamped to the minimum and maximum values. Later, this value will be
// adjusted for time-based acceleration.
if (value > 0) {
return constrain(value * targetVelocity, minimumVelocity, maximumVelocity);
} else {
return -constrain(-value * targetVelocity, minimumVelocity, maximumVelocity);
}
}
float AutoScrollHelper::getEdgeValue(float relativeValue, float size, float maxValue, float current) {
// For now, leading and trailing edges are always the same size.
const float edgeSize = constrain(relativeValue * size, NO_MIN, maxValue);
const float valueLeading = constrainEdgeValue(current, edgeSize);
const float valueTrailing = constrainEdgeValue(size - current, edgeSize);
const float value = (valueTrailing - valueLeading);
float interpolated;
if (value < 0) {
interpolated = -mEdgeInterpolator->getInterpolation(-value);
} else if (value > 0) {
interpolated = mEdgeInterpolator->getInterpolation(value);
} else {
return 0;
}
return constrain(interpolated, -1.f, 1.f);
}
float AutoScrollHelper::constrainEdgeValue(float current, float leading) {
if (leading == 0) return 0;
switch (mEdgeType) {
case EDGE_TYPE_INSIDE:
case EDGE_TYPE_INSIDE_EXTEND:
if (current < leading) {
if (current >= 0) {
// Movement up to the edge is scaled.
return 1.f - current / leading;
} else if (mAnimating && (mEdgeType == EDGE_TYPE_INSIDE_EXTEND)) {
// Movement beyond the edge is always maximum.
return 1.f;
}
}
break;
case EDGE_TYPE_OUTSIDE:
if (current < 0) {
// Movement beyond the edge is scaled.
return current / -leading;
}
break;
}
return 0;
}
void AutoScrollHelper::cancelTargetTouch() {
long eventTime = SystemClock::uptimeMillis();
MotionEvent* cancel = MotionEvent::obtain(
eventTime, eventTime, MotionEvent::ACTION_CANCEL, 0, 0, 0);
mTarget->onTouchEvent(*cancel);
cancel->recycle();
}
void AutoScrollHelper::animationRun(){
if (!mAnimating) {
return;
}
if (mNeedsReset) {
mNeedsReset = false;
mScroller->start();
}
ClampedScroller* scroller = mScroller;
if (scroller->isFinished() || !shouldAnimate()) {
mAnimating = false;
return;
}
if (mNeedsCancel) {
mNeedsCancel = false;
cancelTargetTouch();
}
scroller->computeScrollDelta();
const int deltaX = scroller->getDeltaX();
const int deltaY = scroller->getDeltaY();
scrollTargetBy(deltaX, deltaY);
// Keep going until the scroller has permanently stopped.
mTarget->postOnAnimation(mRunnable);
}
/////////////////////////////////////////////////////////////////////////////////////
AutoScrollHelper::ClampedScroller::ClampedScroller() {
mStartTime = LONG_MIN;//Long.MIN_VALUE;
mStopTime = -1;
mDeltaTime = 0;
mDeltaX = 0;
mDeltaY = 0;
}
void AutoScrollHelper::ClampedScroller::setRampUpDuration(int durationMillis) {
mRampUpDuration = durationMillis;
}
void AutoScrollHelper::ClampedScroller::setRampDownDuration(int durationMillis) {
mRampDownDuration = durationMillis;
}
void AutoScrollHelper::ClampedScroller::start() {
mStartTime = AnimationUtils::currentAnimationTimeMillis();
mStopTime = -1;
mDeltaTime = mStartTime;
mStopValue = 0.5f;
mDeltaX = 0;
mDeltaY = 0;
}
void AutoScrollHelper::ClampedScroller::requestStop() {
long currentTime = AnimationUtils::currentAnimationTimeMillis();
mEffectiveRampDown = constrain((int) (currentTime - mStartTime), 0, mRampDownDuration);
mStopValue = getValueAt(currentTime);
mStopTime = currentTime;
}
bool AutoScrollHelper::ClampedScroller::isFinished() {
return mStopTime > 0
&& AnimationUtils::currentAnimationTimeMillis() > mStopTime + mEffectiveRampDown;
}
float AutoScrollHelper::ClampedScroller::getValueAt(long currentTime) {
if (currentTime < mStartTime) {
return .0f;
} else if (mStopTime < 0 || currentTime < mStopTime) {
long elapsedSinceStart = currentTime - mStartTime;
return 0.5f * constrain(elapsedSinceStart / (float) mRampUpDuration, .0f, 1.f);
} else {
long elapsedSinceEnd = currentTime - mStopTime;
return (1.f - mStopValue) + mStopValue
* constrain(elapsedSinceEnd / (float) mEffectiveRampDown, .0f, 1.f);
}
}
float AutoScrollHelper::ClampedScroller::interpolateValue(float value) {
return -4 * value * value + 4 * value;
}
void AutoScrollHelper::ClampedScroller::computeScrollDelta() {
if (mDeltaTime == 0) {
throw "Cannot compute scroll delta before calling start()";
}
long currentTime = AnimationUtils::currentAnimationTimeMillis();
float value = getValueAt(currentTime);
float scale = interpolateValue(value);
long elapsedSinceDelta = currentTime - mDeltaTime;
mDeltaTime = currentTime;
mDeltaX = (int) (elapsedSinceDelta * scale * mTargetVelocityX);
mDeltaY = (int) (elapsedSinceDelta * scale * mTargetVelocityY);
}
void AutoScrollHelper::ClampedScroller::setTargetVelocity(float x, float y) {
mTargetVelocityX = x;
mTargetVelocityY = y;
}
int AutoScrollHelper::ClampedScroller::getHorizontalDirection()const{
return (int) (mTargetVelocityX / std::abs(mTargetVelocityX));
}
int AutoScrollHelper::ClampedScroller::getVerticalDirection()const{
return (int) (mTargetVelocityY / std::abs(mTargetVelocityY));
}
int AutoScrollHelper::ClampedScroller::getDeltaX()const {
return mDeltaX;
}
int AutoScrollHelper::ClampedScroller::getDeltaY()const {
return mDeltaY;
}
////////////////////////////////////////////////////////////////////////////////////////////
AbsListViewAutoScroller::AbsListViewAutoScroller(AbsListView* target):AutoScrollHelper(target){
mTarget = target;
}
void AbsListViewAutoScroller::scrollTargetBy(int deltaX, int deltaY) {
((AbsListView*)mTarget)->scrollListBy(deltaY);
}
bool AbsListViewAutoScroller::canTargetScrollHorizontally(int direction) {
// List do not scroll horizontally.
return false;
}
bool AbsListViewAutoScroller::canTargetScrollVertically(int direction) {
AbsListView* target = (AbsListView*)mTarget;
int itemCount = target->getCount();
if (itemCount == 0) {
return false;
}
int childCount = target->getChildCount();
int firstPosition = target->getFirstVisiblePosition();
int lastPosition = firstPosition + childCount;
if (direction > 0) {
// Are we already showing the entire last item?
if (lastPosition >= itemCount) {
View* lastView = target->getChildAt(childCount - 1);
if (lastView->getBottom() <= target->getHeight()) {
return false;
}
}
} else if (direction < 0) {
// Are we already showing the entire first item?
if (firstPosition <= 0) {
View* firstView = target->getChildAt(0);
if (firstView->getTop() >= 0) {
return false;
}
}
} else {
// The behavior for direction 0 is undefined and we can return
// whatever we want.
return false;
}
return true;
}
}

126
src/gui/widget/autoscrollhelper.h Executable file
View File

@ -0,0 +1,126 @@
#pragma once
#include <widget/view.h>
#include <limits.h>
namespace cdroid{
class AbsListView;
class AutoScrollHelper{
public:
static constexpr float RELATIVE_UNSPECIFIED = 0;
static constexpr float NO_MAX = 100000.f;//FLT_MAX;//Float.MAX_VALUE;
static constexpr float NO_MIN = 0;
static constexpr int EDGE_TYPE_INSIDE = 0;
static constexpr int EDGE_TYPE_INSIDE_EXTEND = 1;
static constexpr int EDGE_TYPE_OUTSIDE = 2;
private:
static constexpr int HORIZONTAL = 0;
static constexpr int VERTICAL = 1;
static constexpr int DEFAULT_EDGE_TYPE = EDGE_TYPE_INSIDE_EXTEND;
static constexpr int DEFAULT_MINIMUM_VELOCITY_DIPS = 315;
static constexpr int DEFAULT_MAXIMUM_VELOCITY_DIPS = 1575;
static constexpr float DEFAULT_MAXIMUM_EDGE = NO_MAX;
static constexpr float DEFAULT_RELATIVE_EDGE = 0.2f;
static constexpr float DEFAULT_RELATIVE_VELOCITY = 1.f;
static constexpr int DEFAULT_ACTIVATION_DELAY = 10;//ViewConfiguration::getTapTimeout();
static constexpr int DEFAULT_RAMP_UP_DURATION = 500;
static constexpr int DEFAULT_RAMP_DOWN_DURATION = 500;
class ClampedScroller{
private:
int mRampUpDuration;
int mRampDownDuration;
float mTargetVelocityX;
float mTargetVelocityY;
long mStartTime;
long mDeltaTime;
int mDeltaX;
int mDeltaY;
long mStopTime;
float mStopValue;
int mEffectiveRampDown;
float getValueAt(long currentTime);
float interpolateValue(float value);
public:
ClampedScroller();
void setRampUpDuration(int durationMillis);
void setRampDownDuration(int durationMillis);
void start();
void requestStop();
bool isFinished();
void computeScrollDelta();
void setTargetVelocity(float x, float y);
int getHorizontalDirection()const;
int getVerticalDirection()const;
int getDeltaX()const;
int getDeltaY()const;
};
ClampedScroller* mScroller;
Interpolator* mEdgeInterpolator;
protected: View* mTarget;
Runnable mRunnable;
float mRelativeEdges[2];
float mMaximumEdges[2];
int mEdgeType;
int mActivationDelay;
float mRelativeVelocity[2];
float mMinimumVelocity[2];
float mMaximumVelocity[2];
bool mAlreadyDelayed;
/** Whether to reset the scroller start time on the next animation. */
bool mNeedsReset;
/** Whether to send a cancel motion event to the target view. */
bool mNeedsCancel;
/** Whether the auto-scroller is actively scrolling. */
bool mAnimating;
/** Whether the auto-scroller is enabled. */
bool mEnabled;
/** Whether the auto-scroller consumes events when scrolling. */
bool mExclusive;
private:
bool shouldAnimate();
void startAnimating();
void requestStop();
float computeTargetVelocity(int direction, float coordinate, float srcSize, float dstSize);
float getEdgeValue(float relativeValue, float size, float maxValue, float current);
float constrainEdgeValue(float current, float leading);
void cancelTargetTouch();
void animationRun();
public:
AutoScrollHelper(View*target);
virtual ~AutoScrollHelper();
AutoScrollHelper& setEnabled(bool enabled);
bool isEnabled()const;
AutoScrollHelper& setExclusive(bool exclusive);
bool isExclusive()const;
AutoScrollHelper& setMaximumVelocity(float horizontalMax, float verticalMax);
AutoScrollHelper& setMinimumVelocity(float horizontalMin, float verticalMin);
AutoScrollHelper& setRelativeVelocity(float horizontal, float vertical);
AutoScrollHelper& setEdgeType(int type);
AutoScrollHelper& setRelativeEdges(float horizontal, float vertical);
AutoScrollHelper& setMaximumEdges(float horizontalMax, float verticalMax);
AutoScrollHelper& setActivationDelay(int delayMillis);
AutoScrollHelper& setRampUpDuration(int durationMillis);
AutoScrollHelper& setRampDownDuration(int durationMillis);
bool onTouch(View& v, MotionEvent& event);
virtual void scrollTargetBy(int deltaX, int deltaY)=0;
virtual bool canTargetScrollHorizontally(int direction)=0;
virtual bool canTargetScrollVertically(int direction)=0;
};
class AbsListViewAutoScroller:public AutoScrollHelper{
public:
AbsListViewAutoScroller(AbsListView* target);
void scrollTargetBy(int deltaX, int deltaY)override;
bool canTargetScrollHorizontally(int direction)override;
bool canTargetScrollVertically(int direction)override;
};
}

View File

@ -0,0 +1,207 @@
#include <widget/dropdownlistview.h>
#include <widget/textview.h>
namespace cdroid{
DropDownListView::DropDownListView(Context*context,bool hijackfoxus):ListView(context,AttributeSet()){
}
bool DropDownListView::shouldShowSelector(){
return isHovered() || ListView::shouldShowSelector();
}
bool DropDownListView::onTouchEvent(MotionEvent& ev){
if(mResolveHoverRunnable){
removeCallbacks(mResolveHoverRunnable);
mResolveHoverRunnable=nullptr;
}
return ListView::onTouchEvent(ev);
}
bool DropDownListView::onHoverEvent(MotionEvent& ev){
int action = ev.getActionMasked();
if (action == MotionEvent::ACTION_HOVER_EXIT && mResolveHoverRunnable == nullptr) {
// This may be transitioning to TOUCH_DOWN. Postpone drawable state
// updates until either the next frame or the next touch event.
mResolveHoverRunnable = [this](){
mResolveHoverRunnable=nullptr;
drawableStateChanged();
};
post(mResolveHoverRunnable);
}
// Allow the super class to handle hover state management first.
bool handled = ListView::onHoverEvent(ev);
if (action == MotionEvent::ACTION_HOVER_ENTER || action == MotionEvent::ACTION_HOVER_MOVE) {
int position = pointToPosition((int) ev.getX(), (int) ev.getY());
if (position != INVALID_POSITION && position != mSelectedPosition) {
View* hoveredItem = getChildAt(position - getFirstVisiblePosition());
if (hoveredItem->isEnabled()) {
// Force a focus so that the proper selector state gets
// used when we update.
requestFocus();
positionSelector(position, hoveredItem);
setSelectedPositionInt(position);
setNextSelectedPositionInt(position);
}
updateSelectorState();
}
} else {
// Do not cancel the selected position if the selection is visible
// by other means.
if (!ListView::shouldShowSelector()) {
setSelectedPositionInt(INVALID_POSITION);
setNextSelectedPositionInt(INVALID_POSITION);
}
}
return handled;
}
void DropDownListView::drawableStateChanged() {
if (mResolveHoverRunnable == nullptr) {
ListView::drawableStateChanged();
}
}
bool DropDownListView::onForwardedEvent(MotionEvent& event, int activePointerId) {
bool handledEvent = true;
bool bClearPressedItem = false;
int x,y,activeIndex,position;
const int actionMasked = event.getActionMasked();
switch (actionMasked) {
case MotionEvent::ACTION_CANCEL:
handledEvent = false;
break;
case MotionEvent::ACTION_UP:
handledEvent = false;
// $FALL-THROUGH$
case MotionEvent::ACTION_MOVE:
activeIndex = event.findPointerIndex(activePointerId);
if (activeIndex < 0) {
handledEvent = false;
break;
}
x = (int) event.getX(activeIndex);
y = (int) event.getY(activeIndex);
position = pointToPosition(x, y);
if (position == INVALID_POSITION) {
bClearPressedItem = true;
break;
}
View* child = getChildAt(position - getFirstVisiblePosition());
setPressedItem(child, position, x, y);
handledEvent = true;
if (actionMasked == MotionEvent::ACTION_UP) {
long id = getItemIdAtPosition(position);
performItemClick(child, position, id);
}
break;
}
// Failure to handle the event cancels forwarding.
if (!handledEvent || bClearPressedItem) {
clearPressedItem();
}
// Manage automatic scrolling.
if (handledEvent) {
if (mScrollHelper == nullptr) {
mScrollHelper = new AbsListViewAutoScroller(this);
}
mScrollHelper->setEnabled(true);
mScrollHelper->onTouch(*this, event);
} else if (mScrollHelper != nullptr) {
mScrollHelper->setEnabled(false);
}
return handledEvent;
}
void DropDownListView::setListSelectionHidden(bool hideListSelection) {
mListSelectionHidden = hideListSelection;
}
void DropDownListView::clearPressedItem() {
mDrawsInPressedState = false;
setPressed(false);
updateSelectorState();
View* motionView = getChildAt(mMotionPosition - mFirstPosition);
if (motionView) motionView->setPressed(false);
}
void DropDownListView::setPressedItem(View* child, int position, float x, float y) {
mDrawsInPressedState = true;
// Ordering is essential. First, update the container's pressed state.
drawableHotspotChanged(x, y);
if (!isPressed()) {
setPressed(true);
}
// Next, run layout if we need to stabilize child positions.
if (mDataChanged) {
layoutChildren();
}
// Manage the pressed view based on motion position. This allows us to
// play nicely with actual touch and scroll events.
View* motionView = getChildAt(mMotionPosition - mFirstPosition);
if (motionView && motionView != child && motionView->isPressed()) {
motionView->setPressed(false);
}
mMotionPosition = position;
// Offset for child coordinates.
float childX = x - child->getLeft();
float childY = y - child->getTop();
child->drawableHotspotChanged(childX, childY);
if (!child->isPressed()) {
child->setPressed(true);
}
// Ensure that keyboard focus starts from the last touched position.
setSelectedPositionInt(position);
positionSelectorLikeTouch(position, child, x, y);
// Refresh the drawable state to reflect the new pressed state,
// which will also update the selector state.
refreshDrawableState();
}
bool DropDownListView::touchModeDrawsInPressedState() {
return mDrawsInPressedState || ListView::touchModeDrawsInPressedState();
}
View* DropDownListView::obtainView(int position, bool* isScrap) {
View* view = ListView::obtainView(position, isScrap);
if (dynamic_cast<TextView*>(view)) {
//((TextView*) view)->setHorizontallyScrolling(true);
}
return view;
}
bool DropDownListView::isInTouchMode()const{
// WARNING: Please read the comment where mListSelectionHidden is declared
return (mHijackFocus && mListSelectionHidden) || ListView::isInTouchMode();
}
bool DropDownListView::hasWindowFocus()const{
return mHijackFocus || ListView::hasWindowFocus();
}
bool DropDownListView::isFocused()const{
return mHijackFocus || ListView::isFocused();
}
bool DropDownListView::hasFocus()const{
return mHijackFocus || ListView::hasFocus();
}
}

View File

@ -0,0 +1,36 @@
#pragma once
#include <widget/listview.h>
#include <widget/autoscrollhelper.h>
namespace cdroid{
class DropDownListView:public ListView{
private:
bool mListSelectionHidden;
bool mHijackFocus;
bool mDrawsInPressedState;
AbsListViewAutoScroller* mScrollHelper;
Runnable mResolveHoverRunnable;
void clearPressedItem();
void setPressedItem(View* child, int position, float x, float y);
protected:
void drawableStateChanged()override;
bool touchModeDrawsInPressedState()override;
View* obtainView(int position, bool* isScrap)override;
public:
DropDownListView(Context*,bool hijackfoxus);
bool shouldShowSelector()override;
bool onTouchEvent(MotionEvent& ev)override;
bool onHoverEvent(MotionEvent& ev)override;
bool onForwardedEvent(MotionEvent& event, int activePointerId);
void setListSelectionHidden(bool hideListSelection);
bool isInTouchMode()const override;
bool hasWindowFocus()const override;
bool isFocused()const override;
bool hasFocus()const override;
virtual void scrollTargetBy(int deltaX, int deltaY)=0;
virtual bool canTargetScrollHorizontally(int direction)=0;
virtual bool canTargetScrollVertically(int direction)=0;
};
}

View File

@ -17,7 +17,7 @@ LayoutInflater*LayoutInflater::from(Context*context){
return new LayoutInflater(context);
}
LayoutInflater::ViewInflater LayoutInflater::getViewInflater(const std::string&name){
LayoutInflater::ViewInflater LayoutInflater::getInflater(const std::string&name){
std::map<const std::string,ViewInflater>&maps=LayoutInflater::getMap();
auto it=maps.find(name);
return (it!=maps.end())?it->second:nullptr;
@ -64,7 +64,7 @@ typedef struct{
static void startElement(void *userData, const XML_Char *name, const XML_Char **satts){
WindowParserData*pd=(WindowParserData*)userData;
AttributeSet atts(satts);
LayoutInflater::ViewInflater inflater=LayoutInflater::getViewInflater(name);
LayoutInflater::ViewInflater inflater=LayoutInflater::getInflater(name);
ViewGroup*parent=nullptr;
if(pd->views.size())
parent=dynamic_cast<ViewGroup*>(pd->views.back());

View File

@ -15,22 +15,22 @@ private:
static std::map<const std::string,ViewInflater>&getMap();
public:
static LayoutInflater*from(Context*context);
static ViewInflater getViewInflater(const std::string&);
static bool registInflater(const std::string&name,ViewInflater fun);
static ViewInflater getInflater(const std::string&);
View* inflate(std::istream&stream,ViewGroup*root);
View* inflate(const std::string&resource,ViewGroup* root);
View* inflate(const std::string&resource,ViewGroup* root, bool attachToRoot);
};
template<typename T>
class InflateRegister{
class InflaterRegister{
public:
InflateRegister(const std::string&name){
InflaterRegister(const std::string&name){
LayoutInflater::registInflater(name,[](Context*ctx,const AttributeSet&attr)->View*{return new T(ctx,attr);});
}
};
#define DECLARE_WIDGET(T) static InflateRegister<T> widget_inflater_##T(#T);
#define DECLARE_WIDGET(T) static InflaterRegister<T> widget_inflater_##T(#T);
}//endof namespace
#endif

View File

@ -433,7 +433,7 @@ protected:
void invalidateParentIfNeededAndWasQuickRejected();
void destroyDrawingCache();
RefPtr<ImageSurface>getDrawingCache(bool autoScale);
bool hasWindowFocus()const;
virtual bool hasWindowFocus()const;
virtual bool setFrame(int x,int y,int w,int h);
virtual void resetResolvedDrawables();
@ -462,8 +462,6 @@ protected:
bool awakenScrollBars();
bool awakenScrollBars(int startDelay, bool invalidate);
void postOnAnimation(Runnable& action);
void postOnAnimationDelayed(Runnable& action, uint32_t delayMillis);
static int combineVisibility(int vis1, int vis2);
virtual void onSizeChanged(int w,int h,int oldw,int oldh);
virtual void onScrollChanged(int l, int t, int oldl, int oldt);
@ -777,7 +775,7 @@ public:
void setFocusable(bool);
int getFocusable()const;
virtual void unFocus(View*);
bool hasFocus()const;
virtual bool hasFocus()const;
virtual bool restoreFocusInCluster(int direction);
virtual bool restoreFocusNotInCluster();
virtual bool restoreDefaultFocus();
@ -830,6 +828,8 @@ public:
virtual bool onGenericMotionEvent(MotionEvent& event);
virtual void onHoverChanged(bool hovered);
void postOnAnimation(Runnable& action);
void postOnAnimationDelayed(Runnable& action, uint32_t delayMillis);
bool post(Runnable& what);
bool post(const std::function<void()>&what);
bool postDelayed(const std::function<void()>&what,uint32_t delay=0);