add drawable hotspot support

This commit is contained in:
侯歌 2021-10-29 17:09:11 +08:00
parent 916cf9decc
commit ef0b2447f8
28 changed files with 253 additions and 73 deletions

View File

@ -62,6 +62,14 @@ int Drawable::getOpacity(){
return UNKNOWN;
}
void Drawable::setHotspot(float x,float y){
}
void Drawable::setHotspotBounds(int left,int top,int width,int height){
}
void Drawable::getHotspotBounds(Rect&outRect){
outRect=mBounds;
}
std::shared_ptr<Drawable::ConstantState>Drawable::getConstantState(){
return nullptr;
}
@ -327,7 +335,7 @@ static int parseColor(const std::string&value){
}
static void parseShapeGradient(Shape*shape,const AttributeSet&atts){
std::vector<int>cls;
std::vector<uint32_t>cls;
cls.push_back(atts.getColor("startColor"));
if(atts.hasAttribute("centerColor"))

View File

@ -88,7 +88,9 @@ public:
bool setLevel(int level);
int getLevel()const{return mLevel;}
virtual int getOpacity();
virtual void setHotspot(float x,float y);
virtual void setHotspotBounds(int left,int top,int width,int height);
virtual void getHotspotBounds(Rect&outRect);
virtual bool getPadding(Rect&padding);
virtual Insets getOpticalInsets();
virtual bool isStateful()const;

View File

@ -88,12 +88,12 @@ void RippleBackground::onStateChanged(){
mAnimator = ValueAnimator::ofFloat({mOpacity,newOpacity});//this, OPACITY, newOpacity);
mAnimator->setDuration(OPACITY_DURATION);
mAnimator->setInterpolator(new LinearInterpolator());//LINEAR_INTERPOLATOR);
mAnimator->addUpdateListener([this](ValueAnimator&anim){
mAnimator->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
LOGD("mOpacity=%f",fp->getAnimatedValue());
mOpacity=fp->getAnimatedValue();
invalidateSelf();
});
}));
mAnimator->start();
}

View File

@ -15,11 +15,11 @@ protected:
float mDensityScale;
void invalidateSelf();
void onHotspotBoundsChanged();
virtual void onTargetRadiusChanged(float targetRadius);
public:
RippleComponent(RippleDrawable* owner,const Rect& bounds);
void onBoundsChange();
virtual void onHotspotBoundsChanged();
virtual void onBoundsChange();
void setup(float maxRadius, int densityDpi);
void getBounds(Rect& bounds);
};

View File

@ -51,7 +51,7 @@ RippleDrawable::RippleDrawable(std::shared_ptr<RippleState> state) {
RippleDrawable::RippleDrawable(ColorStateList* color,Drawable* content,Drawable* mask)
:RippleDrawable(std::make_shared<RippleState>(nullptr,nullptr)){
if(content)addLayer(content,{0},0,0,0,0,0);
if(content)addLayer(content,{0},-1,0,0,0,0);
if(mask)addLayer(mask,{0},MASK_LAYER_ID,0,0,0,0);
setColor(color);
ensurePadding();
@ -134,7 +134,7 @@ void RippleDrawable::onBoundsChange(const Rect& bounds){
if (!mOverrideBounds) {
mHotspotBounds=bounds;
//onHotspotBoundsChanged();
onHotspotBoundsChanged();
}
for (auto ripple:mExitingRipples) {
@ -261,6 +261,40 @@ void RippleDrawable::tryRippleExit(){
}
void RippleDrawable::clearHotspots(){
if(mRipple){
mRipple->end();
mRipple =nullptr;
mRippleActive =false;
}
if(mBackground)
mBackground->setState(false,false,false);
cancelExitingRipples();
}
void RippleDrawable::setHotspot(float x,float y){
mHasPending=(mRipple==nullptr)||(mBackground ==nullptr);
if (mHasPending){
mPendingX =x;
mPendingY = y;
}
if(mRipple)mRipple->move(x,y);
}
void RippleDrawable::setHotspotBounds(int left,int top,int w,int h){
mOverrideBounds = true;
mHotspotBounds.set(left,top,w,h);
onHotspotBoundsChanged();
}
void RippleDrawable::getHotspotBounds(Rect&out){
out=mHotspotBounds;
}
void RippleDrawable::onHotspotBoundsChanged(){
for(auto ripple:mExitingRipples)
ripple->onHotspotBoundsChanged();
if(mRipple)mRipple->onHotspotBoundsChanged();
if(mBackground)mBackground->onHotspotBoundsChanged();
}
void RippleDrawable::draw(Canvas& canvas){
@ -307,7 +341,36 @@ void RippleDrawable::pruneRipples() {
for (int i = remaining; i < count; i++) {
delete mExitingRipples[i];
}
mExitingRipples.clear();
mExitingRipples.resize(remaining);
}
int RippleDrawable::getMaskType(){
if (mRipple == nullptr && mExitingRipples.size()<= 0
&& (mBackground == nullptr || !mBackground->isVisible())) {
// We might need a mask later.
return MASK_UNKNOWN;
}
if (mMask) {
if (mMask->getOpacity() == OPAQUE) {
// Clipping handles opaque explicit masks.
return MASK_NONE;
} else {
return MASK_EXPLICIT;
}
}
// Check for non-opaque, non-mask content.
std::vector<ChildDrawable*>& array = mLayerState->mChildren;
int count = array.size();
for (int i = 0; i < count; i++) {
if (array[i]->mDrawable->getOpacity() != OPAQUE) {
return MASK_CONTENT;
}
}
// Clipping handles opaque content.
return MASK_NONE;
}
void RippleDrawable::drawContent(Canvas& canvas) {
@ -331,9 +394,7 @@ void RippleDrawable::drawBackgroundAndRipples(Canvas& canvas) {
const float x = mHotspotBounds.centerX();
const float y = mHotspotBounds.centerY();
canvas.translate(x, y);
int color=mState->mColor->getColorForState(getState(),0xFF000000);
if((color>>24)>0x80)color=(color&0x00FFFFFF)|0x80000000;
int color=mState->mColor->getColorForState(getState(),0xFF888888);
canvas.set_color(color);
if (mBackground && mBackground->isVisible()) {
@ -341,6 +402,9 @@ void RippleDrawable::drawBackgroundAndRipples(Canvas& canvas) {
}
for (auto ripple:mExitingRipples) {
int alpha=0x80*ripple->getOpacity();
color=(color&0x00FFFFFF)|(alpha<<24);
canvas.set_color(color);
ripple->draw(canvas,1.f);
}

View File

@ -53,7 +53,9 @@ private:
void tryRippleEnter();
void tryRippleExit();
void clearHotspots();
void onHotspotBoundsChanged();
void updateLocalState();
int getMaskType();
void drawContent(Canvas& canvas);
void drawBackgroundAndRipples(Canvas& canvas);
void drawMask(Canvas& canvas);
@ -74,6 +76,9 @@ public:
bool setDrawableByLayerId(int id, Drawable* drawable)override;
void setPaddingMode(int mode);
bool canApplyTheme()override;
void getHotspotBounds(Rect&out)override;
void setHotspot(float x,float y)override;
void setHotspotBounds(int left,int top,int w,int h)override;
void draw(Canvas& canvas);
void invalidateSelf()override;
void invalidateSelf(bool invalidateMask);

View File

@ -20,7 +20,7 @@ RippleForeground::RippleForeground(RippleDrawable* owner,const Rect& bounds, flo
void RippleForeground::onTargetRadiusChanged(float targetRadius){
clampStartingPosition();
//switchToUiThreadAnimation();
invalidateSelf();//switchToUiThreadAnimation();
}
void RippleForeground::drawSoftware(Canvas& c,float origAlpha) {
@ -29,23 +29,24 @@ void RippleForeground::drawSoftware(Canvas& c,float origAlpha) {
if (alpha > 0 && radius > 0) {
const float x = getCurrentX();
const float y = getCurrentY();
c.arc(x, y, radius,0,M_PI*2.);
c.arc(x,y, radius,0,M_PI*2.);
c.fill();
}
}
void RippleForeground::pruneSwFinished() {
for (auto it=mRunningSwAnimators.begin();it!=mRunningSwAnimators.end();){
if ((*it)->isRunning()) {
if (!(*it)->isRunning()) {
Animator*anim=(*it);
it=mRunningSwAnimators.erase(it);
}else it++;
}
}
void RippleForeground::getBounds(Rect& bounds) {
int outerX = (int) mTargetX;
int outerY = (int) mTargetY;
int r = (int) mTargetRadius + 1;
const int outerX = (int) mTargetX;
const int outerY = (int) mTargetY;
const int r = (int) mTargetRadius + 1;
bounds.set(outerX - r, outerY - r, r+r,r + r);
}
@ -60,72 +61,76 @@ bool RippleForeground::hasFinishedExit()const{
}
long RippleForeground::computeFadeOutDelay() {
long timeSinceEnter = SystemClock::uptimeMillis() - mEnterStartedAtMillis;
long timeSinceEnter = AnimationUtils::currentAnimationTimeMillis() - mEnterStartedAtMillis;
if (timeSinceEnter > 0 && timeSinceEnter < OPACITY_HOLD_DURATION) {
return OPACITY_HOLD_DURATION - timeSinceEnter;
}
return 0;
}
float RippleForeground::getOpacity()const{
return mOpacity;
}
void RippleForeground::startSoftwareEnter() {
for (auto anim:mRunningSwAnimators) {
anim->cancel();
delete anim;
}
mRunningSwAnimators.clear();
ValueAnimator* tweenRadius = ValueAnimator::ofFloat({.0f,1.f});//this, TWEEN_RADIUS, 1);
tweenRadius->setDuration(RIPPLE_ENTER_DURATION);
tweenRadius->setInterpolator(new DecelerateInterpolator());//DECELERATE_INTERPOLATOR);
tweenRadius->addUpdateListener([this](ValueAnimator&anim){
tweenRadius->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
LOGV("mTweenRadius=%f",fp->getAnimatedValue());
LOGV("mTweenRadius=%f [%f,%f,%f] opacity=%f",fp->getAnimatedValue(),getCurrentRadius(),mStartRadius,mTargetRadius,mOpacity);
mTweenRadius=fp->getAnimatedValue();
onAnimationPropertyChanged();
});
}));
tweenRadius->start();
mRunningSwAnimators.push_back(tweenRadius);
ValueAnimator* tweenOrigin = ValueAnimator::ofFloat({.0f,1.f});//this, TWEEN_ORIGIN, 1);
tweenOrigin->setDuration(RIPPLE_ORIGIN_DURATION);
tweenOrigin->setInterpolator(new DecelerateInterpolator());//DECELERATE_INTERPOLATOR);
tweenOrigin->addUpdateListener([this](ValueAnimator&anim){
tweenOrigin->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
mTweenX=mTweenY=fp->getAnimatedValue();
onAnimationPropertyChanged();
});
}));
tweenOrigin->start();
mRunningSwAnimators.push_back(tweenOrigin);
ValueAnimator* opacity = ValueAnimator::ofFloat({.0f,1.f});//this, OPACITY, 1);
opacity->setDuration(OPACITY_ENTER_DURATION);
opacity->setInterpolator(new LinearInterpolator());//LINEAR_INTERPOLATOR);
opacity->addUpdateListener([this](ValueAnimator&anim){
opacity->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
mOpacity=fp->getAnimatedValue();
onAnimationPropertyChanged();
});
}));
opacity->start();
mRunningSwAnimators.push_back(opacity);
}
void RippleForeground::startSoftwareExit() {
ValueAnimator* opacity = ValueAnimator::ofFloat({1.f,.0f});
ValueAnimator* opacity = ValueAnimator::ofFloat({.0f,1.f});
opacity->setDuration(OPACITY_EXIT_DURATION);
opacity->setInterpolator(new LinearInterpolator());//LINEAR_INTERPOLATOR);
opacity->addListener(mAnimationListener);
opacity->setStartDelay(computeFadeOutDelay());
opacity->addUpdateListener([this](ValueAnimator&anim){
opacity->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
mOpacity=fp->getAnimatedValue();
});
}));
opacity->start();
mRunningSwAnimators.push_back(opacity);
}
void RippleForeground::enter(){
mEnterStartedAtMillis = SystemClock::uptimeMillis();
mEnterStartedAtMillis = AnimationUtils::currentAnimationTimeMillis();
startSoftwareEnter();
}
@ -150,10 +155,6 @@ void RippleForeground::end(){
anim->end();
}
mRunningSwAnimators.clear();
/*for (auto animhw:mRunningHwAnimators;) {
animhw->end();
}
mRunningHwAnimators.clear();*/
}
void RippleForeground::onAnimationPropertyChanged() {
@ -165,13 +166,6 @@ void RippleForeground::onAnimationPropertyChanged() {
void RippleForeground::draw(Canvas&canvas,float alpha){
pruneSwFinished();
drawSoftware(canvas,alpha);
/*const bool hasDisplayListCanvas = !mForceSoftware && c instanceof DisplayListCanvas;
if (hasDisplayListCanvas) {
DisplayListCanvas hw = (DisplayListCanvas) c;
drawHardware(hw, p);
} else {
drawSoftware(c, p);
}*/
}
void RippleForeground::clampStartingPosition(){

View File

@ -1,5 +1,6 @@
#pragma once
#include <drawables/ripplebackground.h>
#include <animation/animationutils.h>
namespace cdroid{
class RippleForeground:public RippleComponent{
@ -57,6 +58,7 @@ public:
void move(float x, float y);
bool hasFinishedExit()const;
long computeFadeOutDelay();
float getOpacity()const;
void enter();
void exit();
void end();

View File

@ -2,7 +2,6 @@
#include <color.h>
#include <cdlog.h>
namespace cdroid{
Shape::Shape(){
@ -62,7 +61,7 @@ int Shape::getStroke(int& color)const{
return mStrokeWidth;
}
void Shape::setGradientColors(const std::vector<int>&cls){
void Shape::setGradientColors(const std::vector<uint32_t>&cls){
mGradientColors=cls;
mRebuildGradient=true;
}

View File

@ -16,7 +16,7 @@ protected:
float mCenterX,mCenterY;
bool mRebuildGradient;
bool bUseLevel;
std::vector<int>mGradientColors;//size 0:nofill, 1:solid fill 2,3:gradient fill
std::vector<uint32_t>mGradientColors;//size 0:nofill, 1:solid fill 2,3:gradient fill
RefPtr<Pattern>mPaint;//used to fill
void rebuildPattern(const Rect&r);
virtual void onResize(int width,int height){}
@ -34,7 +34,7 @@ public:
void setStrokeDash(int width,int gap);
int getStroke(int &color)const;//return line width,color
void setSolidColor(int color);
void setGradientColors(const std::vector<int>&cls);//gradient fill colors
void setGradientColors(const std::vector<uint32_t>&cls);//gradient fill colors
void setGradientAngle(float angle);
void setGradientRadius(float radius);
void setGradientType(int gt);

View File

@ -529,7 +529,7 @@ void AbsListView::positionSelector(int position, View* sel, bool manageHotspot,
}
updateSelectorState();
}
//if (manageHotspot) selector->setHotspot(x, y);
if (manageHotspot) selector->setHotspot(x, y);
}
}
@ -1186,7 +1186,7 @@ void AbsListView::checkTap(int x,int y) {
((TransitionDrawable*) d)->resetTransition();
}
}
//mSelector->setHotspot(x, y);
mSelector->setHotspot(x, y);
}
if (longClickable) {
@ -2027,6 +2027,13 @@ bool AbsListView::onKeyUp(int keyCode, KeyEvent& event) {
}
void AbsListView::dispatchSetPressed(bool pressed) {
// Don't dispatch setPressed to our children. We call setPressed on ourselves to
// get the selector in the right state, but we don't want to press each child.
}
void AbsListView::dispatchDrawableHotspotChanged(float x, float y){
// Don't dispatch hotspot changes to children. We'll manually handle
// calling drawableHotspotChanged on the correct child.
}
int AbsListView::pointToPosition(int x, int y) {
@ -2705,7 +2712,7 @@ void AbsListView::onTouchUp(MotionEvent&ev) {
if (d && dynamic_cast<TransitionDrawable*>(d)) {
((TransitionDrawable*) d)->resetTransition();
}
//mSelector->setHotspot(x, ev.getY());
mSelector->setHotspot(x, ev.getY());
}
if (mTouchModeReset != nullptr) {
removeCallbacks(mTouchModeReset);

View File

@ -321,6 +321,7 @@ public:
bool verifyDrawable(Drawable* dr)const override;
void jumpDrawablesToCurrentState()override;
void dispatchDrawableHotspotChanged(float x, float y)override;
int pointToPosition(int x, int y);
long pointToRowId(int x, int y);
bool performItemClick(View* view, int position, long id)override;

View File

@ -101,6 +101,11 @@ void AbsSeekBar::drawableStateChanged(){
}
}
void AbsSeekBar::drawableHotspotChanged(float x,float y){
ProgressBar::drawableHotspotChanged(x,y);
if(mThumb)mThumb->setHotspot(x,y);
}
bool AbsSeekBar::verifyDrawable(Drawable* who)const{
return who == mThumb || who == mTickMark || ProgressBar::verifyDrawable(who);
}

View File

@ -66,6 +66,7 @@ public:
virtual void setMax(int max);
virtual bool canUserSetProgress()const;
void onRtlPropertiesChanged(int layoutDirection)override;
void drawableHotspotChanged(float x,float y)override;
};
}//namespace

View File

@ -37,6 +37,11 @@ void CompoundButton::drawableStateChanged() {
}
}
void CompoundButton::drawableHotspotChanged(float x,float y){
Button::drawableHotspotChanged(x,y);
if(mButtonDrawable)mButtonDrawable->setHotspot(x,y);
}
int CompoundButton::getHorizontalOffsetForDrawables()const{
return (mButtonDrawable == nullptr) ?0: mButtonDrawable->getIntrinsicWidth();
}

View File

@ -35,6 +35,7 @@ public:
void setOnCheckedChangeListener(OnCheckedChangeListener listener);
/*OnCheckedChangeWidgetListener internal use(for radiogroup...)*/
void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener);
void drawableHotspotChanged(float x,float y)override;
};
}
#endif

View File

@ -307,6 +307,7 @@ bool ImageView::setFrame(int l, int t, int w, int h){
configureBounds();
return changed;
}
void ImageView::configureBounds(){
if (mDrawable == nullptr /*|| !mHaveFrame*/) return;
const int mPaddingLeft=0,mPaddingRight=0,mPaddingTop=0,mPaddingBottom=0;
@ -390,6 +391,19 @@ void ImageView::configureBounds(){
mDrawMatrix.xx,mDrawMatrix.yx,mDrawMatrix.xy,mDrawMatrix.yy,mDrawMatrix.x0,mDrawMatrix.y0);
}
void ImageView::drawableStateChanged(){
View::drawableStateChanged();
if(mDrawable && mDrawable->isStateful()
&& mDrawable->setState(getDrawableState())){
invalidateDrawable(*mDrawable);
}
}
void ImageView::drawableHotspotChanged(float x, float y){
View::drawableHotspotChanged(x,y);
if(mDrawable)mDrawable->setHotspot(x,y);
}
void ImageView::invalidateDrawable(Drawable& dr){
if (mDrawable==&dr) {
// update cached drawable dimensions if they've changed

View File

@ -55,8 +55,8 @@ protected:
void configureBounds();
bool setFrame(int l, int t, int w, int h)override;
void onMeasure(int widthMeasureSpec, int heightMeasureSpec)override;
void drawableStateChanged()override;
virtual void onDraw(Canvas& canvas) override;
public:
public:
explicit ImageView(int w, int h);
ImageView(Context*ctx,const AttributeSet&attrs);
@ -97,6 +97,7 @@ public:
void setImageLevel(int level);
void setSelected(bool selected)override;
void setImageState(const std::vector<int>&state, bool merge);
void drawableHotspotChanged(float x, float y)override;
};
}

View File

@ -129,6 +129,12 @@ void ProgressBar::drawableStateChanged(){
updateDrawableState();
}
void ProgressBar::drawableHotspotChanged(float x, float y){
View::drawableHotspotChanged(x,y);
if(mProgressDrawable)mProgressDrawable->setHotspot(x,y);
if(mIndeterminateDrawable)mIndeterminateDrawable->setHotspot(x,y);
}
bool ProgressBar::verifyDrawable(Drawable* who)const{
return who == mProgressDrawable || who == mIndeterminateDrawable|| View::verifyDrawable(who);
}
@ -179,10 +185,10 @@ void ProgressBar::doRefreshProgress(int id, int progress, bool fromUser,bool cal
mAnimator->setAutoCancel(true);
mAnimator->setDuration(PROGRESS_ANIM_DURATION);
mAnimator->setInterpolator(new DecelerateInterpolator());
mAnimator->addUpdateListener([this](ValueAnimator&anim){
mAnimator->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
setVisualProgress(ID_PRIMARY,fp->getAnimatedValue());
});
}));
}
prop=(FloatPropertyValuesHolder*)mAnimator->getValues(0);
prop->setValues({scale});

View File

@ -124,6 +124,7 @@ public:
int getSecondaryProgressTintMode()const;
Drawable* getTintTarget(int layerId, bool shouldFallback);
void setProgressDrawableTiled(Drawable* d);
void drawableHotspotChanged(float x, float y)override;
};
}

View File

@ -511,10 +511,10 @@ void TabLayout::ensureScrollAnimator(){
mScrollAnimator = new ValueAnimator();
mScrollAnimator->setInterpolator(new FastOutSlowInInterpolator());
mScrollAnimator->setDuration(ANIMATION_DURATION);
mScrollAnimator->addUpdateListener([this](ValueAnimator&anim) {
mScrollAnimator->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim) {
IntPropertyValuesHolder*ip=(IntPropertyValuesHolder*)anim.getValues()[0];
scrollTo(ip->getAnimatedValue(), 0);
});
}));
}
}
@ -1240,10 +1240,10 @@ void TabLayout::SlidingTabStrip::animateIndicatorToPosition(int position, int du
ValueAnimator* animator = mIndicatorAnimator;
animator->setDuration(duration);
animator->removeAllListeners();
animator->addUpdateListener([this,startLeft,targetLeft,startRight,targetRight](ValueAnimator&anim) {
animator->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this,startLeft,targetLeft,startRight,targetRight](ValueAnimator&anim) {
const float fraction = anim.getAnimatedFraction();
setIndicatorPosition(lerp(startLeft, targetLeft, fraction),lerp(startRight, targetRight, fraction));
});
}));
Animator::AnimatorListener al;
al.onAnimationEnd=[this,position](Animator&anim,bool reverse){
mSelectedPosition = position;

View File

@ -939,6 +939,7 @@ void TextView::setCompoundDrawablesWithIntrinsicBounds(const std::string& left,
setCompoundDrawablesWithIntrinsicBounds(context->getDrawable(left),context->getDrawable(top),
context->getDrawable(right),context->getDrawable(bottom));
}
void TextView::drawableStateChanged(){
View::drawableStateChanged();
@ -958,6 +959,14 @@ void TextView::drawableStateChanged(){
}
}
void TextView::drawableHotspotChanged(float x,float y){
View::drawableHotspotChanged(x,y);
for(int i=0;mDrawables&&(i<4);i++){
Drawable* dr=mDrawables->mShowing[i];
if(dr)dr->setHotspot(x,y);
}
}
void TextView::updateTextColors(){
bool inval = false;
const std::vector<int>&drawableState = getDrawableState();

View File

@ -199,6 +199,7 @@ public:
std::vector<Drawable*>getCompoundDrawables();
void setCompoundDrawableTintList(ColorStateList* tint);
ColorStateList* getCompoundDrawableTintList();
void drawableHotspotChanged(float x,float y)override;
void setCompoundDrawableTintMode(int tintMode);
int getCompoundDrawableTintMode();

View File

@ -2828,6 +2828,18 @@ void View::drawableStateChanged(){
if(changed) invalidate(true);
}
void View::drawableHotspotChanged(float x, float y){
if(mBackground)mBackground->setHotspot(x,y);
if(mDefaultFocusHighlight)mDefaultFocusHighlight->setHotspot(x,y);
if(mForegroundInfo && mForegroundInfo->mDrawable)
mForegroundInfo->mDrawable->setHotspot(x,y);
dispatchDrawableHotspotChanged(x,y);
}
void View::dispatchDrawableHotspotChanged(float x,float y){
//NOTHING
}
void View::refreshDrawableState(){
mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;
drawableStateChanged();
@ -3543,7 +3555,7 @@ void View::setPressed(bool pressed){
}
void View::setPressed(bool pressed,int x,int y){
//if(pressed)drawableHotspotChanged(x,y);
if(pressed)drawableHotspotChanged(x,y);
setPressed(pressed);
}
@ -4753,7 +4765,7 @@ bool View::onTouchEvent(MotionEvent& event){
}
break;
case MotionEvent::ACTION_MOVE:
//if (clickable)drawableHotspotChanged(x, y);
if (clickable)drawableHotspotChanged(x, y);
// Be lenient about moving outside of buttons
if (!pointInView(x, y,ViewConfiguration::get(mContext).getScaledTouchSlop())) {

View File

@ -587,6 +587,8 @@ public:
bool isFocusableInTouchMode()const;
virtual void setFocusable(int focusable);
virtual void setFocusableInTouchMode(bool focusableInTouchMode);
virtual void drawableHotspotChanged(float x, float y);
virtual void dispatchDrawableHotspotChanged(float x,float y);
void refreshDrawableState();
bool isDefaultFocusHighlightNeeded(const Drawable* background,const Drawable* foreground)const;
virtual const std::vector<int>getDrawableState();

View File

@ -731,6 +731,18 @@ void ViewGroup::dispatchSetPressed(bool pressed) {
}
}
void ViewGroup::dispatchDrawableHotspotChanged(float x,float y){
for(auto child:mChildren){
const bool nonActivationable = !child->isClickable()&&!child->isLongClickable();
const bool duplicateState = (child->mViewFlags & DUPLICATE_PARENT_STATE)!=0;
if( nonActivationable||duplicateState){
float point[2]={x,y};
transformPointToViewLocal(point,*child);
child->drawableHotspotChanged(point[0],point[1]);
}
}
}
int ViewGroup::getChildCount()const{
return mChildren.size();
}

View File

@ -193,6 +193,7 @@ protected:
void drawableStateChanged()override;
std::vector<int> onCreateDrawableState()const override;
void dispatchSetPressed(bool pressed)override;
void dispatchDrawableHotspotChanged(float x,float y)override;
virtual int getChildDrawingOrder(int childCount, int i);
std::vector<View*> buildOrderedChildList();

View File

@ -22,14 +22,25 @@ TEST_F(ANIMATOR,callback){
app.exec();
}
TEST_F(ANIMATOR,ofInt){
TEST_F(ANIMATOR,ofInt1){
ValueAnimator*anim=ValueAnimator::ofInt({0,100});
anim->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
IntPropertyValuesHolder*ip=(IntPropertyValuesHolder*)anim.getValues(0);
LOGD("value=%d",ip->getAnimatedValue());
}));
for(int i=0;i<=10;i++){
anim->setCurrentFraction((float)i/10.f);
}
}
TEST_F(ANIMATOR,ofInt2){
IntPropertyValuesHolder iprop;
iprop.setValues({0,100});
ValueAnimator*anim=ValueAnimator::ofPropertyValuesHolder({&iprop});
anim->addUpdateListener([this](ValueAnimator&anim){
anim->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
IntPropertyValuesHolder*ip=(IntPropertyValuesHolder*)anim.getValues(0);
LOGD("value=%d",ip->getAnimatedValue());
});
}));
for(int i=0;i<=10;i++){
anim->setCurrentFraction((float)i/10.f);
}
@ -39,10 +50,26 @@ TEST_F(ANIMATOR,ofFloat){
FloatPropertyValuesHolder fprop;
fprop.setValues({0,100});
ValueAnimator*anim=ValueAnimator::ofPropertyValuesHolder({&fprop});
anim->addUpdateListener([this](ValueAnimator&anim){
anim->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
LOGD("value=%f",fp->getAnimatedValue());
});
}));
for(int i=0;i<=10;i++){
anim->setCurrentFraction((float)i/10.f);
}
}
class MyProperty: public Property{
public:
MyProperty(const std::string&name):Property(name){
}
void set(void* object, float value)override{
LOGD("value=%f",value);
}
};
TEST_F(ANIMATOR,ofProperty){
MyProperty*myprop=new MyProperty("test");
ObjectAnimator*anim=ObjectAnimator::ofInt(nullptr,myprop,{0,100});
for(int i=0;i<=10;i++){
anim->setCurrentFraction((float)i/10.f);
}
@ -54,10 +81,10 @@ TEST_F(ANIMATOR,loopdrivered){
iprop.setValues({0,100});
ValueAnimator*anim=ValueAnimator::ofPropertyValuesHolder({&iprop});
anim->addUpdateListener([this](ValueAnimator&anim){
anim->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
IntPropertyValuesHolder*fp=(IntPropertyValuesHolder*)anim.getValues(0);
LOGD("value=%d",fp->getAnimatedValue());
});
}));
anim->setDuration(2000);
anim->start();
app.exec();
@ -82,14 +109,14 @@ TEST_F(ANIMATOR,translate){
cprop.setValues({0xFF000000,0xFFFF8844});
ValueAnimator*anim=ValueAnimator::ofPropertyValuesHolder({&xprop,&yprop,&cprop});
anim->addUpdateListener([tv](ValueAnimator&anim){
anim->addUpdateListener(ValueAnimator::AnimatorUpdateListener([tv](ValueAnimator&anim){
IntPropertyValuesHolder*xp=(IntPropertyValuesHolder*)anim.getValues(0);
IntPropertyValuesHolder*yp=(IntPropertyValuesHolder*)anim.getValues(1);
ColorPropertyValuesHolder*cp=(ColorPropertyValuesHolder*)anim.getValues(2);
tv->setPos(xp->getAnimatedValue(),tv->getTop());
tv->setPos(tv->getLeft(),yp->getAnimatedValue());
tv->setBackgroundColor(cp->getAnimatedValue());
});
}));
anim->setDuration(5000);
anim->start();
@ -107,12 +134,12 @@ TEST_F(ANIMATOR,scale){
fprop.setValues({0,2.0});
ValueAnimator*anim=ValueAnimator::ofPropertyValuesHolder({&fprop});
anim->addUpdateListener([tv](ValueAnimator&anim){
anim->addUpdateListener(ValueAnimator::AnimatorUpdateListener([tv](ValueAnimator&anim){
FloatPropertyValuesHolder*fp=(FloatPropertyValuesHolder*)anim.getValues(0);
float scale=fp->getAnimatedValue();
tv->setScaleX(scale);
tv->setScaleY(scale);
});
}));
anim->setDuration(5000);
anim->start();
app.exec();