add switch(a button with on/off animation)

This commit is contained in:
侯歌 2022-02-17 11:33:54 +08:00
parent 67538a268b
commit 842bf7cb70
14 changed files with 1200 additions and 125 deletions

View File

@ -17,7 +17,7 @@ public:
const std::string getString(const std::string&resid)const;
public:
Builder(Context* context);
~Builder();
~Builder();
Context* getContext();
Builder& setTitle(const std::string& title);
Builder& setCustomTitle(View* customTitleView);

View File

@ -275,69 +275,6 @@ void Surface::write_to_jpg_stream(const SlotWriteFunc& write_func){
#endif
}
typedef struct{
unsigned char a;
unsigned char r;
unsigned char g;
unsigned char b;
}PRGB;
static PRGB get_pixel(ImageSurface*img,int x,int y){
unsigned char*data=img->get_data()+img->get_stride()*y+x*4;
PRGB r={data[3],data[2],data[1],data[0]};
return r;
}
static bool isStretchableMarker(PRGB&p){
return (p.a>>7)&1;
}
int ImageSurface::get_ninepatch(std::vector<NinePatchBlock>&horz,std::vector<NinePatchBlock>&vert){
int i;
PRGB c;
int width=get_width();
int height=get_height();
int pad[4]={-1,-1,-1,-1};
horz.clear();
vert.clear();
#define check_pixel(x,y) c=get_pixel(this,x,y); if( (c.a!=0) && (c.a+c.r+c.g+c.b)!=4) return 0;
check_pixel(0,0);
check_pixel(width-1,0);
check_pixel(0,height-1);
check_pixel(width-1,height-1);
//horz stretch infos
int pos=0;
int horz_stretch=0;
int vert_stretch=0;
PRGB last,next;
last=get_pixel(this,1,0);
for(int x=1;x<width-1;x++){
next=get_pixel(this,x+1,0);
if(isStretchableMarker(last)!=isStretchableMarker(next)||x==width-2){
bool stretchable=isStretchableMarker(last);
int len=x-pos;
horz.push_back(NinePatchBlock(pos,len,stretchable));
//LOGV("horz:%d,%d,%d",pos,len,stretchable);
if(stretchable)horz_stretch+=len;
last=next;pos=x;
}
}
//vert streatch infos
pos=0;
last=get_pixel(this,0,1);
for(int y=1;y<height-1;y++){
next=get_pixel(this,0,y+1);
if(isStretchableMarker(last)!=isStretchableMarker(next)||y==height-2){
bool stretchable = isStretchableMarker(last);
int len = y - pos;
//LOGV("vert:%d,%d,%d",pos,len,stretchable);
vert.push_back(NinePatchBlock(pos,len,stretchable));
if (stretchable)vert_stretch += len;
last = next;
pos = y;
}
}
return horz_stretch<<16|vert_stretch;
}
#if defined(ENABLE_JPEG)||defined(ENABLE_TURBOJPEG)
RefPtr<ImageSurface> ImageSurface::create_from_jpg(std::string filename){
auto cobject = cairo_image_surface_create_from_jpeg(filename.c_str());

View File

@ -636,14 +636,6 @@ protected:
*
* Note that like all surfaces, an ImageSurface is a reference-counted object that should be used via Cairo::RefPtr.
*/
struct NinePatchBlock{
int pos;
int len;
bool stretchable;
NinePatchBlock(int p,int l,bool s){
pos=p;len=l;stretchable=s;
}
};
class CAIROMM_API ImageSurface : public Surface
{
protected:
@ -696,7 +688,6 @@ public:
* @since 1.2
*/
int get_stride() const;
int get_ninepatch(std::vector<NinePatchBlock>&horz,std::vector<NinePatchBlock>&vert);
/**
* This function provides a stride value that will respect all alignment

View File

@ -174,6 +174,7 @@ void Canvas::draw_image(const RefPtr<ImageSurface>&img,const RECT&dst,const RECT
restore();
}
#if 0
void Canvas::draw_ninepatch(const RefPtr<ImageSurface>img,const RECT& rect,const std::vector<NinePatchBlock>&horz,
const std::vector<NinePatchBlock>& vert){
int dw=rect.width;
@ -226,6 +227,7 @@ void Canvas::draw_ninepatch(const RefPtr<ImageSurface>img,const RECT& rect,const
dy0=dy1;
}
}
#endif
void Canvas::dump2png(const char*fname){
#ifdef CAIRO_HAS_PNG_FUNCTIONS

View File

@ -39,8 +39,6 @@ public:
void rectangle(int x,int y,int w,int h);
void rectangle(const RECT &r);
void draw_image(const RefPtr<ImageSurface>&img,const RECT&dst,const RECT*src);
void draw_ninepatch(const RefPtr<ImageSurface>img,const RECT& rect,const std::vector<NinePatchBlock>&horz,
const std::vector<NinePatchBlock>&vert);
void rotate(float degrees,float px,float py);
void dump2png(const char*fname);
};

22
src/gui/core/mathutils.h Executable file
View File

@ -0,0 +1,22 @@
#ifndef __MATH_UTILS_H__
#define __MATH_UTILS_H__
#include <math.h>
namespace cdroid{
class MathUtils{
public:
template<typename T>
static T constrain(T value, T min, T max) {
if (value > max) {
return max;
} else if (value < min) {
return min;
} else {
return value;
}
}
};
}
#endif

View File

@ -3,6 +3,7 @@
#include <string.h>
#include <cdtypes.h>
#include <cdlog.h>
#include <limits.h>
namespace cdroid{
@ -892,6 +893,10 @@ void VelocityTracker::addMovement(const MotionEvent& event) {
LOGV("%f,%f",event.getX(),event.getY());
}
void VelocityTracker::computeCurrentVelocity(int32_t units){
computeCurrentVelocity(units,INT_MAX);
}
void VelocityTracker::computeCurrentVelocity(int32_t units, float maxVelocity) {
BitSet32 idBits(mVelocityTracker->getCurrentPointerIdBits());
mCalculatedIdBits = idBits;

View File

@ -55,6 +55,7 @@ public:
explicit VelocityTracker(const char* strategy);
void clear();
void addMovement(const MotionEvent& event);
void computeCurrentVelocity(int32_t units);
void computeCurrentVelocity(int32_t units, float maxVelocity);
void getVelocity(int32_t id, float* outVx, float* outVy);
bool getEstimator(int32_t id, Estimator* outEstimator);

View File

@ -5,50 +5,6 @@
namespace cdroid{
//https://github.com/soramimi/QtNinePatch/blob/master/NinePatch.cpp
NinePatchDrawable::NinePatchState::NinePatchState(){
mTint = nullptr;
mBaseAlpha=1.0f;
mDither = true;
mTint = nullptr;
mTintMode = DEFAULT_TINT_MODE;
mChangingConfigurations = 0;
mAutoMirrored =false;
mPadding.set(0,0,0,0);
mOpticalInsets.set(0,0,0,0);
}
NinePatchDrawable::NinePatchState::NinePatchState(RefPtr<ImageSurface>bitmap,const Rect*padding)
:NinePatchDrawable::NinePatchState(){
bitmap->get_ninepatch(mHorz,mVert);
if(padding)
mPadding=*padding;
mNinePatch=bitmap;
mPadding.set(0,0,0,0);
}
NinePatchDrawable::NinePatchState::NinePatchState(const NinePatchState&orig){
mTint = orig.mTint;
mNinePatch=orig.mNinePatch;
mTintMode = orig.mTintMode;
mPadding = orig.mPadding;
mOpticalInsets = orig.mOpticalInsets;
mBaseAlpha = orig.mBaseAlpha;
mDither = orig.mDither;
mHorz =orig.mHorz;
mVert =orig.mVert;
mChangingConfigurations=orig.mChangingConfigurations;
mAutoMirrored = orig.mAutoMirrored;
//mThemeAttrs = orig.mThemeAttrs;
}
Drawable*NinePatchDrawable::NinePatchState::newDrawable(){
return new NinePatchDrawable(shared_from_this());
}
int NinePatchDrawable::NinePatchState::getChangingConfigurations()const{
return mChangingConfigurations|(mTint ? mTint->getChangingConfigurations() : 0);
}
NinePatchDrawable::NinePatchDrawable(std::shared_ptr<NinePatchState>state){
mNinePatchState=state;
mAlpha=255;
@ -220,7 +176,7 @@ void NinePatchDrawable::draw(Canvas&canvas){
canvas.scale(-1.f,1.f);
canvas.translate(cx,cy);
}
canvas.draw_ninepatch(mNinePatchState->mNinePatch,mBounds,mNinePatchState->mHorz,mNinePatchState->mVert);
mNinePatchState->draw(canvas,mBounds);
canvas.restore();
}
}
@ -233,5 +189,165 @@ Drawable*NinePatchDrawable::inflate(Context*ctx,const AttributeSet&atts){
return new NinePatchDrawable(bmp);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
NinePatchDrawable::NinePatchState::NinePatchState(){
mTint = nullptr;
mBaseAlpha=1.0f;
mDither = true;
mTint = nullptr;
mTintMode = DEFAULT_TINT_MODE;
mChangingConfigurations = 0;
mAutoMirrored =false;
mPadding.set(0,0,0,0);
mOpticalInsets.set(0,0,0,0);
}
NinePatchDrawable::NinePatchState::NinePatchState(RefPtr<ImageSurface>bitmap,const Rect*padding)
:NinePatchDrawable::NinePatchState(){
if(padding)
mPadding=*padding;
mNinePatch=bitmap;
get_ninepatch();
mPadding.set(0,0,0,0);
LOGD("ninpatch %p size=%dx%d",this,bitmap->get_width(),bitmap->get_height());
for(auto h:mHorz)LOGD("%p HORZ %d,%d,%d)",this,h.pos,h.len,h.stretchable);
for(auto v:mVert)LOGD("%p VERT %d,%d,%d)",this,v.pos,v.len,v.stretchable);
}
NinePatchDrawable::NinePatchState::NinePatchState(const NinePatchState&orig){
mTint = orig.mTint;
mNinePatch=orig.mNinePatch;
mTintMode = orig.mTintMode;
mPadding = orig.mPadding;
mOpticalInsets = orig.mOpticalInsets;
mBaseAlpha = orig.mBaseAlpha;
mDither = orig.mDither;
mHorz =orig.mHorz;
mVert =orig.mVert;
mChangingConfigurations=orig.mChangingConfigurations;
mAutoMirrored = orig.mAutoMirrored;
//mThemeAttrs = orig.mThemeAttrs;
}
Drawable*NinePatchDrawable::NinePatchState::newDrawable(){
return new NinePatchDrawable(shared_from_this());
}
int NinePatchDrawable::NinePatchState::getChangingConfigurations()const{
return mChangingConfigurations|(mTint ? mTint->getChangingConfigurations() : 0);
}
static unsigned int get_pixel(RefPtr<ImageSurface>img,int x,int y){
unsigned int*data=(unsigned int*)(img->get_data()+y*img->get_stride());
return data[x];
}
static bool isStretchableMarker(unsigned int px){
return (px>>24)==0xFF;
}
int NinePatchDrawable::NinePatchState::get_ninepatch(){
int i;
int width =mNinePatch->get_width();
int height=mNinePatch->get_height();
int pad[4]={-1,-1,-1,-1};
mHorz.clear();
mVert.clear();
#define check_pixel(x,y) c=get_pixel(this,x,y); if( (c.a!=0) && (c.a+c.r+c.g+c.b)!=4) return 0;
/*check_pixel(0,0);
check_pixel(width-1,0);
check_pixel(0,height-1);
check_pixel(width-1,height-1);*/
//horz stretch infos
int pos=1;
int horz_stretch=0;
int vert_stretch=0;
unsigned int last,next;
last=get_pixel(mNinePatch,1,0);
for(int x=1;x<width-1;x++){
next=get_pixel(mNinePatch,x+1,0);
if(isStretchableMarker(last)!=isStretchableMarker(next)||x==width-2){
bool stretchable=isStretchableMarker(last);
int len=x-pos+1;
DIV d={pos,len,stretchable};
mHorz.push_back(d);
//LOGV("horz:%d,%d,%d",pos,len,stretchable);
if(stretchable)horz_stretch+=len;
last=next;pos=x;
}
}
//vert streatch infos
pos=1;
last=get_pixel(mNinePatch,0,1);
for(int y=1;y<height-1;y++){
next=get_pixel(mNinePatch,0,y+1);
if(isStretchableMarker(last)!=isStretchableMarker(next)||y==height-2){
bool stretchable = isStretchableMarker(last);
int len = y - pos+1;
//LOGV("vert:%d,%d,%d",pos,len,stretchable);
DIV d={pos,len,stretchable};
mVert.push_back(d);
if (stretchable)vert_stretch += len;
last = next;
pos = y;
}
}
return horz_stretch<<16|vert_stretch;
}
void NinePatchDrawable::NinePatchState::draw(Canvas&canvas,const Rect&rect){
int dw=rect.width;
int dh=rect.height;
int sw=mNinePatch->get_width();
int sh=mNinePatch->get_height();
float horz_stretch=0;
float vert_stretch=0;
float horz_mul=0,vert_mul =0;
int dy0=0, dy1=0;
float vstretch=0;
for_each(mHorz.begin(),mHorz.end(),[&](const DIV&v){if(v.stretchable)horz_stretch+=v.len;});
for_each(mVert.begin(),mVert.end(),[&](const DIV&v){if(v.stretchable)vert_stretch+=v.len;});
if (horz_stretch > 0) horz_mul = (float)(dw - (sw - 2 - horz_stretch)) / horz_stretch;
if (vert_stretch > 0) vert_mul = (float)(dh - (sh - 2 - vert_stretch)) / vert_stretch;
for(int i=0;i<(int)mVert.size();i++){
int sy0=mVert[i].pos;
if(i+1==(int)mVert.size()){
dy1=dh;
}else if(mVert[i].stretchable){
vstretch=(float)mVert[i].len*vert_mul;
float s=floor(vstretch);
vstretch-=s;
dy1+=(int)s;
}else{
dy1+=mVert[i].len;
}
int dx0=0,dx1=0;
float hstretch=0;
for(int j=0;j<(int)mHorz.size();j++){
int sx0=mHorz[j].pos;
if(j+1==(int)mHorz.size()){
dx1=dw;
}else if(mHorz[j].stretchable){
hstretch+=(float)mHorz[j].len*horz_mul;
float s=floor(hstretch);
hstretch-=s;
dx1+=(int)s;
}else{
dx1+=mHorz[j].len;
}
RECT rd={dx0,dy0,dx1-dx0+1,dy1-dy0+1};
RECT rs={sx0, sy0,mHorz[j].len,mVert[i].len};
rd.offset(rect.left,rect.top);
canvas.draw_image(mNinePatch,rd,&rs);
LOGV("%p(%d,%d,%d,%d)->(%d,%d,%d,%d)",this,rs.left,rs.top,rs.width,rs.height,
rd.left,rd.top,rd.width,rd.height);
dx0=dx1;
}
dy0=dy1;
}
}
}

View File

@ -5,7 +5,14 @@ namespace cdroid{
class NinePatchDrawable:public Drawable{
private:
struct DIV{
int pos;
int len;
bool stretchable;
};
class NinePatchState:public std::enable_shared_from_this<NinePatchState>,public ConstantState{
private:
int get_ninepatch();
public:
float mBaseAlpha;// = 1.0f;
bool mDither;// = DEFAULT_DITHER;
@ -14,14 +21,15 @@ private:
Insets mOpticalInsets;
int mTintMode;
int mChangingConfigurations;
std::vector<NinePatchBlock> mHorz;
std::vector<NinePatchBlock> mVert;
std::vector<DIV> mHorz;
std::vector<DIV> mVert;
ColorStateList*mTint;
RefPtr<ImageSurface>mNinePatch;
NinePatchState();
NinePatchState(const NinePatchState&state);
NinePatchState(RefPtr<ImageSurface>bitmap,const Rect*padding=nullptr);
Drawable*newDrawable()override;
void draw(Canvas&canvas,const Rect&rect);
int getChangingConfigurations()const override;
};
int mAlpha;

842
src/gui/widget/switch.cc Executable file
View File

@ -0,0 +1,842 @@
#include <widget/switch.h>
#include <core/soundeffect.h>
#include <core/mathutils.h>
#include <widget/viewgroup.h>
namespace cdroid{
DECLATE_WIDGET2(Switch,"cdroid:attr/switchStyle")
Switch::Switch(Context* context,const AttributeSet& a)
:CompoundButton(context,a){
mThumbDrawable = context->getDrawable(a.getString("thumb"));
if (mThumbDrawable) {
mThumbDrawable->setCallback(this);
}
mTrackDrawable = context->getDrawable(a.getString("track"));
if (mTrackDrawable) {
mTrackDrawable->setCallback(this);
}
mTextOn = a.getString("textOn");
mTextOff = a.getString("textOff");
mShowText = a.getBoolean("showText", true);
mThumbTextPadding = a.getDimensionPixelSize("thumbTextPadding", 0);
mSwitchMinWidth = a.getDimensionPixelSize("switchMinWidth", 0);
mSwitchPadding = a.getDimensionPixelSize("switchPadding", 0);
mSplitTrack = a.getBoolean("splitTrack", false);
mUseFallbackLineSpacing = true;//context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.P;
ColorStateList* thumbTintList = context->getColorStateList(a.getString("thumbTint"));
if (thumbTintList) {
mThumbTintList = thumbTintList;
mHasThumbTint = true;
}
/*BlendMode thumbTintMode = Drawable.parseBlendMode( a.getInt(com.android.internal.R.styleable.Switch_thumbTintMode, -1),null);
if (mThumbBlendMode != thumbTintMode) {
//mThumbBlendMode = thumbTintMode;
mHasThumbTintMode = true;
}*/
if (mHasThumbTint || mHasThumbTintMode) {
applyThumbTint();
}
ColorStateList* trackTintList = context->getColorStateList("trackTint");
if (trackTintList) {
mTrackTintList = trackTintList;
mHasTrackTint = true;
}
/*BlendMode trackTintMode = Drawable.parseBlendMode(a.getInt(com.android.internal.R.styleable.Switch_trackTintMode, -1), null);
if (mTrackBlendMode != trackTintMode) {
mTrackBlendMode = trackTintMode;
mHasTrackTintMode = true;
}*/
if (mHasTrackTint || mHasTrackTintMode) {
applyTrackTint();
}
/*appearance = a.getResourceId( com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
if (appearance != 0) {
setSwitchTextAppearance(context, appearance);
}*/
ViewConfiguration& config = ViewConfiguration::get(context);
mTouchSlop = config.getScaledTouchSlop();
mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
// Refresh display with current params
refreshDrawableState();
// Default state is derived from on/off-text, so state has to be updated when on/off-text
// are updated.
//setDefaultStateDescription();
setChecked(isChecked());
}
void Switch::init(){
mTextColors = nullptr;
mThumbDrawable = nullptr;
mThumbTintList = nullptr;
mTrackDrawable = nullptr;
mTrackTintList = nullptr;
mVelocityTracker = nullptr;
mPositionAnimator = nullptr;
mOnLayout = mOffLayout = nullptr;
}
void Switch::setSwitchTextAppearance(Context* context,const std::string&resid){
AttributeSet atts=context->obtainStyledAttributes(resid);//com.android.internal.R.styleable.TextAppearance);
int ts;
ColorStateList* colors=nullptr;
//colors = appearance.getColorStateList(com.android.internal.R.styleable.TextAppearance_textColor);
if (colors) {
mTextColors = colors;
} else {
// If no color set in TextAppearance, default to the view's textColor
mTextColors = getTextColors();
}
/*ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.TextAppearance_textSize, 0);
if (ts != 0) {
if (ts != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(ts);
requestLayout();
}
}
int typefaceIndex, styleIndex;
typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
TextAppearance_typeface, -1);
styleIndex = appearance.getInt(com.android.internal.R.styleable.
TextAppearance_textStyle, -1);
setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
bool allCaps = appearance.getBoolean(com.android.internal.R.styleable.
TextAppearance_textAllCaps, false);
if (allCaps) {
mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
mSwitchTransformationMethod.setLengthChangesAllowed(true);
} else {
mSwitchTransformationMethod = null;
}*/
}
void Switch::setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex){
}
void Switch::setSwitchPadding(int pixels) {
mSwitchPadding = pixels;
requestLayout();
}
int Switch::getSwitchPadding()const{
return mSwitchPadding;
}
void Switch::setSwitchMinWidth(int pixels) {
mSwitchMinWidth = pixels;
requestLayout();
}
int Switch::getSwitchMinWidth()const{
return mSwitchMinWidth;
}
void Switch::setThumbTextPadding(int pixels) {
mThumbTextPadding = pixels;
requestLayout();
}
int Switch::getThumbTextPadding()const{
return mThumbTextPadding;
}
void Switch::setTrackDrawable(Drawable* track) {
if (mTrackDrawable)
mTrackDrawable->setCallback(nullptr);
mTrackDrawable = track;
if (track)
track->setCallback(this);
requestLayout();
}
void Switch::setTrackResource(const std::string& resId){
setTrackDrawable(getContext()->getDrawable(resId));
}
Drawable* Switch::getTrackDrawable() {
return mTrackDrawable;
}
void Switch::setTrackTintList(ColorStateList* tint){
mTrackTintList = tint;
mHasTrackTint = true;
applyTrackTint();
}
ColorStateList* Switch::getTrackTintList() {
return mTrackTintList;
}
void Switch::setTrackTintMode(PorterDuffMode tintMode){
}
void Switch::applyTrackTint(){
if (mTrackDrawable && (mHasTrackTint || mHasTrackTintMode)) {
mTrackDrawable = mTrackDrawable->mutate();
if (mHasTrackTint) {
mTrackDrawable->setTintList(mTrackTintList);
}
if (mHasTrackTintMode) {
//mTrackDrawable->setTintBlendMode(mTrackBlendMode);
}
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (mTrackDrawable->isStateful()) {
mTrackDrawable->setState(getDrawableState());
}
}
}
PorterDuffMode Switch::getTrackTintMode()const{
return (PorterDuffMode)0;
}
void Switch::setThumbDrawable(Drawable* thumb){
if (mThumbDrawable) {
mThumbDrawable->setCallback(nullptr);
}
mThumbDrawable = thumb;
if (thumb) {
thumb->setCallback(this);
}
requestLayout();
}
void Switch::setThumbResource(const std::string& resId){
setThumbDrawable(getContext()->getDrawable(resId));
}
Drawable* Switch::getThumbDrawable(){
return mThumbDrawable;
}
void Switch::setThumbTintList(ColorStateList* tint){
mThumbTintList = tint;
mHasThumbTint = true;
applyThumbTint();
}
ColorStateList* Switch::getThumbTintList()const{
return mThumbTintList;
}
void Switch::setThumbTintMode(PorterDuffMode tintMode){
}
PorterDuffMode Switch::getThumbTintMode()const{
return (PorterDuffMode)0;
}
void Switch::applyThumbTint() {
if (mThumbDrawable && (mHasThumbTint || mHasThumbTintMode)) {
mThumbDrawable = mThumbDrawable->mutate();
if (mHasThumbTint) {
mThumbDrawable->setTintList(mThumbTintList);
}
//if (mHasThumbTintMode) mThumbDrawable->setTintBlendMode(mThumbBlendMode);
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (mThumbDrawable->isStateful()) {
mThumbDrawable->setState(getDrawableState());
}
}
}
void Switch::setSplitTrack(bool splitTrack){
mSplitTrack = splitTrack;
invalidate();
}
bool Switch::getSplitTrack()const{
return mSplitTrack;
}
std::string Switch::getTextOn()const{
return mTextOn;
}
void Switch::setTextOn(const std::string&text){
mTextOn=text;
invalidate();
}
std::string Switch::getTextOff()const{
return mTextOff;
}
void Switch::setTextOff(const std::string&text){
mTextOff=text;
invalidate();
}
bool Switch::getShowText()const{
return mShowText;
}
void Switch::setShowText(bool showText) {
if (mShowText != showText) {
mShowText = showText;
requestLayout();
}
}
void Switch::onMeasure(int widthMeasureSpec, int heightMeasureSpec){
if (mShowText) {
if (mOnLayout == nullptr) {
mOnLayout = makeLayout(mTextOn);
}
if (mOffLayout == nullptr) {
mOffLayout = makeLayout(mTextOff);
}
}
Rect padding;
int thumbWidth;
int thumbHeight;
if (mThumbDrawable) {
// Cached thumb width does not include padding.
mThumbDrawable->getPadding(padding);
thumbWidth = mThumbDrawable->getIntrinsicWidth() - padding.left - padding.width;
thumbHeight = mThumbDrawable->getIntrinsicHeight();
} else {
thumbWidth = 0;
thumbHeight = 0;
}
int maxTextWidth;
if (mShowText) {
maxTextWidth = std::max(mOnLayout->getMaxLineWidth(), mOffLayout->getMaxLineWidth())
+ mThumbTextPadding * 2;
} else {
maxTextWidth = 0;
}
mThumbWidth = std::max(maxTextWidth, thumbWidth);
int trackHeight;
if (mTrackDrawable) {
mTrackDrawable->getPadding(padding);
trackHeight = mTrackDrawable->getIntrinsicHeight();
} else {
padding.setEmpty();
trackHeight = 0;
}
// Adjust left and right padding to ensure there's enough room for the
// thumb's padding (when present).
int paddingLeft = padding.left;
int paddingRight = padding.width;
if (mThumbDrawable) {
Insets inset = mThumbDrawable->getOpticalInsets();
paddingLeft = std::max(paddingLeft, inset.left);
paddingRight = std::max(paddingRight, inset.right);
}
int switchWidth = std::max(mSwitchMinWidth,
2 * mThumbWidth + paddingLeft + paddingRight);
int switchHeight = std::max(trackHeight, thumbHeight);
mSwitchWidth = switchWidth;
mSwitchHeight = switchHeight;
CompoundButton::onMeasure(widthMeasureSpec, heightMeasureSpec);
int measuredHeight = getMeasuredHeight();
if (measuredHeight < switchHeight) {
setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
}
}
Layout* Switch::makeLayout(const std::string& text){
return nullptr;
}
bool Switch::hitThumb(float x, float y) {
if (mThumbDrawable == nullptr) {
return false;
}
// Relies on mTempRect, MUST be called first!
const int thumbOffset = getThumbOffset();
Rect mTempRect;
mThumbDrawable->getPadding(mTempRect);
const int thumbTop = mSwitchTop - mTouchSlop;
const int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
const int thumbRight = thumbLeft + mThumbWidth + mTempRect.left + mTempRect.width + mTouchSlop;
const int thumbBottom = mSwitchBottom + mTouchSlop;
return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
}
bool Switch::onTouchEvent(MotionEvent& ev){
mVelocityTracker->addMovement(ev);
const int action = ev.getActionMasked();
const float x = ev.getX();
const float y = ev.getY();
switch (action) {
case MotionEvent::ACTION_DOWN:
if (isEnabled() && hitThumb(x, y)) {
mTouchMode = TOUCH_MODE_DOWN;
mTouchX = x;
mTouchY = y;
}
break;
case MotionEvent::ACTION_MOVE:
switch (mTouchMode) {
case TOUCH_MODE_IDLE:
// Didn't target the thumb, treat normally.
break;
case TOUCH_MODE_DOWN:
if (abs(x - mTouchX) > mTouchSlop ||
abs(y - mTouchY) > mTouchSlop) {
mTouchMode = TOUCH_MODE_DRAGGING;
getParent()->requestDisallowInterceptTouchEvent(true);
mTouchX = x;
mTouchY = y;
return true;
}
break;
case TOUCH_MODE_DRAGGING: {
const int thumbScrollRange = getThumbScrollRange();
const float thumbScrollOffset = x - mTouchX;
float dPos;
if (thumbScrollRange != 0) {
dPos = thumbScrollOffset / thumbScrollRange;
} else {
// If the thumb scroll range is empty, just use the
// movement direction to snap on or off.
dPos = thumbScrollOffset > 0 ? 1 : -1;
}
if (isLayoutRtl()) {
dPos = -dPos;
}
const float newPos = MathUtils::constrain(mThumbPosition + dPos, .0f, 1.f);
if (newPos != mThumbPosition) {
mTouchX = x;
setThumbPosition(newPos);
}
return true;
}
}
break;
case MotionEvent::ACTION_UP:
case MotionEvent::ACTION_CANCEL:
if (mTouchMode == TOUCH_MODE_DRAGGING) {
stopDrag(ev);
// Allow super class to handle pressed state, etc.
CompoundButton::onTouchEvent(ev);
return true;
}
mTouchMode = TOUCH_MODE_IDLE;
mVelocityTracker->clear();
break;
}
return CompoundButton::onTouchEvent(ev);
}
void Switch::cancelSuperTouch(MotionEvent& ev){
MotionEvent* cancel = MotionEvent::obtain(ev);
cancel->setAction(MotionEvent::ACTION_CANCEL);
CompoundButton::onTouchEvent(*cancel);
cancel->recycle();
}
void Switch::stopDrag(MotionEvent& ev){
mTouchMode = TOUCH_MODE_IDLE;
// Commit the change if the event is up and not canceled and the switch
// has not been disabled during the drag.
const bool commitChange = ev.getAction() == MotionEvent::ACTION_UP && isEnabled();
const bool oldState = isChecked();
bool newState;
if (commitChange) {
mVelocityTracker->computeCurrentVelocity(1000);
const float xvel = mVelocityTracker->getXVelocity();
if (abs(xvel) > mMinFlingVelocity) {
newState = isLayoutRtl() ? (xvel < 0) : (xvel > 0);
} else {
newState = getTargetCheckedState();
}
} else {
newState = oldState;
}
if (newState != oldState) {
playSoundEffect(SoundEffectConstants::CLICK);
}
// Always call setChecked so that the thumb is moved back to the correct edge
setChecked(newState);
cancelSuperTouch(ev);
}
void Switch::animateThumbToCheckedState(bool newCheckedState){
const float targetPosition = newCheckedState ? 1 : 0;
mPositionAnimator = ObjectAnimator::ofFloat(this,"thumbpos",{mThumbPosition,targetPosition});
mPositionAnimator->setDuration(THUMB_ANIMATION_DURATION);
mPositionAnimator->setAutoCancel(true);
mPositionAnimator->start();
mPositionAnimator->addUpdateListener(ValueAnimator::AnimatorUpdateListener([this](ValueAnimator&anim){
setThumbPosition(anim.getAnimatedValue().get<float>());
}));
}
void Switch::cancelPositionAnimator(){
if (mPositionAnimator) {
mPositionAnimator->cancel();
}
}
bool Switch::getTargetCheckedState()const{
return mThumbPosition > 0.5f;
}
void Switch::setThumbPosition(float position){
mThumbPosition = position;
invalidate();
}
void Switch::toggle(){
setChecked(!isChecked());
}
std::string Switch::getButtonStateDescription()const{
if (isChecked()) {
return mTextOn;// == null ? getResources().getString(R.string.capital_on) : mTextOn;
} else {
return mTextOff;// == null ? getResources().getString(R.string.capital_off) : mTextOff;
}
}
void Switch::setChecked(bool checked){
CompoundButton::setChecked(checked);
// Calling the super method may result in setChecked() getting called
// recursively with a different value, so load the REAL value...
checked = isChecked();
if (isAttachedToWindow() && isLaidOut()) {
animateThumbToCheckedState(checked);
} else {
// Immediately move the thumb to the new position.
cancelPositionAnimator();
setThumbPosition(checked ? 1 : 0);
}
}
void Switch::onLayout(bool changed, int left, int top, int width, int height){
CompoundButton::onLayout(changed, left, top, width,height);
int opticalInsetLeft = 0;
int opticalInsetRight = 0;
if (mThumbDrawable) {
Rect trackPadding;
if (mTrackDrawable) {
mTrackDrawable->getPadding(trackPadding);
} else {
trackPadding.set(0,0,0,0);
}
Insets insets = mThumbDrawable->getOpticalInsets();
opticalInsetLeft = std::max(0, insets.left - trackPadding.left);
opticalInsetRight= std::max(0, insets.right - trackPadding.width);
}
int switchRight;
int switchLeft;
if (isLayoutRtl()) {
switchLeft = getPaddingLeft() + opticalInsetLeft;
switchRight = switchLeft + mSwitchWidth - opticalInsetLeft - opticalInsetRight;
} else {
switchRight = getWidth() - getPaddingRight() - opticalInsetRight;
switchLeft = switchRight - mSwitchWidth + opticalInsetLeft + opticalInsetRight;
}
int switchTop;
int switchBottom;
switch (getGravity() & Gravity::VERTICAL_GRAVITY_MASK) {
default:
case Gravity::TOP:
switchTop = getPaddingTop();
switchBottom = switchTop + mSwitchHeight;
break;
case Gravity::CENTER_VERTICAL:
switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
mSwitchHeight / 2;
switchBottom = switchTop + mSwitchHeight;
break;
case Gravity::BOTTOM:
switchBottom = getHeight() - getPaddingBottom();
switchTop = switchBottom - mSwitchHeight;
break;
}
mSwitchLeft = switchLeft;
mSwitchTop = switchTop;
mSwitchBottom = switchBottom;
mSwitchRight = switchRight;
}
void Switch::draw(Canvas& c) {
Rect padding;
const int switchLeft = mSwitchLeft;
const int switchTop = mSwitchTop;
const int switchRight = mSwitchRight;
const int switchBottom = mSwitchBottom;
int thumbInitialLeft = switchLeft + getThumbOffset();
Insets thumbInsets;
if (mThumbDrawable) {
thumbInsets = mThumbDrawable->getOpticalInsets();
} else {
thumbInsets = Insets::NONE;
}
// Layout the track.
if (mTrackDrawable) {
mTrackDrawable->getPadding(padding);
// Adjust thumb position for track padding.
thumbInitialLeft += padding.left;
// If necessary, offset by the optical insets of the thumb asset.
int trackLeft = switchLeft;
int trackTop = switchTop;
int trackRight = switchRight;
int trackBottom = switchBottom;
if (thumbInsets != Insets::NONE) {
if (thumbInsets.left > padding.left) {
trackLeft += thumbInsets.left - padding.left;
}
if (thumbInsets.top > padding.top) {
trackTop += thumbInsets.top - padding.top;
}
if (thumbInsets.right > padding.width) {
trackRight -= thumbInsets.right - padding.width;
}
if (thumbInsets.bottom > padding.height) {
trackBottom -= thumbInsets.bottom - padding.height;
}
}
mTrackDrawable->setBounds(trackLeft, trackTop, trackRight, trackBottom);
}
// Layout the thumb.
if (mThumbDrawable) {
mThumbDrawable->getPadding(padding);
const int thumbLeft = thumbInitialLeft - padding.left;
const int thumbRight = thumbInitialLeft + mThumbWidth + padding.width;
const int thumbWidth = thumbRight - thumbLeft;
mThumbDrawable->setBounds(thumbLeft, switchTop, thumbWidth, switchBottom-switchTop);
Drawable* background = getBackground();
if (background) {
background->setHotspotBounds(thumbLeft, switchTop, thumbWidth, switchBottom-switchTop);
}
}
// Draw the background.
CompoundButton::draw(c);
}
void Switch::onDraw(Canvas& canvas) {
CompoundButton::onDraw(canvas);
Rect padding;
if (mTrackDrawable) {
mTrackDrawable->getPadding(padding);
} else {
padding.setEmpty();
}
const int switchTop = mSwitchTop;
const int switchBottom = mSwitchBottom;
const int switchInnerTop = switchTop + padding.top;
const int switchInnerBottom = switchBottom - padding.height;
Drawable* thumbDrawable = mThumbDrawable;
if (mTrackDrawable) {
if (mSplitTrack && mThumbDrawable) {
Insets insets = mThumbDrawable->getOpticalInsets();
padding = mThumbDrawable->getBounds();
padding.left += insets.left;
padding.width -= insets.right;
canvas.save();
//canvas.clipRect(padding, Op.DIFFERENCE);
mTrackDrawable->draw(canvas);
canvas.restore();
} else {
mTrackDrawable->draw(canvas);
}
}
canvas.save();
if (mThumbDrawable) {
mThumbDrawable->draw(canvas);
}
Layout* switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
if (switchText) {
std::vector<int> drawableState = getDrawableState();
if (mTextColors) {
canvas.set_color(mTextColors->getColorForState(drawableState, 0));
}
//mTextPaint.drawableState = drawableState;
int cX;
if (thumbDrawable) {
Rect bounds = thumbDrawable->getBounds();
cX = bounds.left + bounds.width;
} else {
cX = getWidth();
}
const int left = cX / 2 - switchText->getMaxLineWidth() / 2;
const int top = (switchInnerTop + switchInnerBottom) / 2 - switchText->getHeight() / 2;
canvas.translate(left, top);
switchText->draw(canvas);
}
canvas.restore();
}
int Switch::getCompoundPaddingLeft() {
if (!isLayoutRtl()) {
return CompoundButton::getCompoundPaddingLeft();
}
int padding = CompoundButton::getCompoundPaddingLeft() + mSwitchWidth;
if (!getText().empty()){//!TextUtils.isEmpty(getText())) {
padding += mSwitchPadding;
}
return padding;
}
int Switch::getCompoundPaddingRight() {
if (isLayoutRtl()) {
return CompoundButton::getCompoundPaddingRight();
}
int padding = CompoundButton::getCompoundPaddingRight() + mSwitchWidth;
if (!getText().empty()){//!TextUtils.isEmpty(getText())) {
padding += mSwitchPadding;
}
return padding;
}
/**
* Translates thumb position to offset according to current RTL setting and
* thumb scroll range. Accounts for both track and thumb padding.
*
* @return thumb offset
*/
int Switch::getThumbOffset() {
float thumbPosition;
if (isLayoutRtl()) {
thumbPosition = 1 - mThumbPosition;
} else {
thumbPosition = mThumbPosition;
}
return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
}
int Switch::getThumbScrollRange() {
if (mTrackDrawable) {
Rect padding;
mTrackDrawable->getPadding(padding);
Insets insets;
if (mThumbDrawable) {
insets = mThumbDrawable->getOpticalInsets();
} else {
insets = Insets::NONE;
}
return mSwitchWidth - mThumbWidth - padding.left - padding.width
- insets.left - insets.right;
} else {
return 0;
}
}
std::vector<int> Switch::onCreateDrawableState()const {
std::vector<int> drawableState = CompoundButton::onCreateDrawableState();
if (isChecked()) {
mergeDrawableStates(drawableState,StateSet::get(StateSet::VIEW_STATE_CHECKED));
}
return drawableState;
}
void Switch::drawableStateChanged() {
CompoundButton::drawableStateChanged();
std::vector<int> state = getDrawableState();
bool changed = false;
if (mThumbDrawable && mThumbDrawable->isStateful()) {
changed |= mThumbDrawable->setState(state);
}
if (mTrackDrawable && mTrackDrawable->isStateful()) {
changed |= mTrackDrawable->setState(state);
}
if (changed) {
invalidate();
}
}
void Switch::drawableHotspotChanged(float x, float y) {
CompoundButton::drawableHotspotChanged(x, y);
if (mThumbDrawable) {
mThumbDrawable->setHotspot(x, y);
}
if (mTrackDrawable) {
mTrackDrawable->setHotspot(x, y);
}
}
bool Switch::verifyDrawable(Drawable* who)const{
return CompoundButton::verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
}
void Switch::jumpDrawablesToCurrentState() {
CompoundButton::jumpDrawablesToCurrentState();
if (mThumbDrawable) {
mThumbDrawable->jumpToCurrentState();
}
if (mTrackDrawable) {
mTrackDrawable->jumpToCurrentState();
}
if (mPositionAnimator && mPositionAnimator->isStarted()) {
mPositionAnimator->end();
mPositionAnimator = nullptr;
}
}
}//endof namespace

153
src/gui/widget/switch.h Executable file
View File

@ -0,0 +1,153 @@
#ifndef __SWITCH_H__
#define __SWITCH_H__
#include <widget/compoundbutton.h>
namespace cdroid{
typedef void* Typeface;
class Switch:public CompoundButton{
private:
static constexpr int THUMB_ANIMATION_DURATION = 250;
static constexpr int TOUCH_MODE_IDLE = 0;
static constexpr int TOUCH_MODE_DOWN = 1;
static constexpr int TOUCH_MODE_DRAGGING = 2;
Drawable* mThumbDrawable;
ColorStateList* mThumbTintList;
//BlendMode mThumbBlendMode = null;
bool mHasThumbTint = false;
bool mHasThumbTintMode = false;
Drawable* mTrackDrawable;
ColorStateList* mTrackTintList;
//BlendMode* mTrackBlendMode = null;
bool mHasTrackTint = false;
bool mHasTrackTintMode = false;
int mThumbTextPadding;
int mSwitchMinWidth;
int mSwitchPadding;
bool mSplitTrack;
std::string mTextOn;
std::string mTextOff;
bool mShowText;
bool mUseFallbackLineSpacing;
int mTouchMode;
int mTouchSlop;
float mTouchX;
float mTouchY;
VelocityTracker* mVelocityTracker;
int mMinFlingVelocity;
float mThumbPosition;
/**
* Width required to draw the switch track and thumb. Includes padding and
* optical bounds for both the track and thumb.
*/
int mSwitchWidth;
/**
* Height required to draw the switch track and thumb. Includes padding and
* optical bounds for both the track and thumb.
*/
int mSwitchHeight;
/**
* Width of the thumb's content region. Does not include padding or
* optical bounds.
*/
int mThumbWidth;
/** Left bound for drawing the switch track and thumb. */
int mSwitchLeft;
/** Top bound for drawing the switch track and thumb. */
int mSwitchTop;
/** Right bound for drawing the switch track and thumb. */
int mSwitchRight;
/** Bottom bound for drawing the switch track and thumb. */
int mSwitchBottom;
ColorStateList* mTextColors;
Layout* mOnLayout;
Layout* mOffLayout;
//TransformationMethod2 mSwitchTransformationMethod;
ObjectAnimator* mPositionAnimator;
private:
void init();
void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex);
void applyTrackTint();
void applyThumbTint();
Layout* makeLayout(const std::string& text);
bool hitThumb(float x, float y);
void cancelSuperTouch(MotionEvent& ev);
void stopDrag(MotionEvent& ev);
void animateThumbToCheckedState(bool newCheckedState);
void cancelPositionAnimator();
bool getTargetCheckedState()const;
void setThumbPosition(float position);
int getThumbOffset();
int getThumbScrollRange();
protected:
void onLayout(bool changed, int left, int top, int width, int height)override;
void onDraw(Canvas&)override;
std::vector<int>onCreateDrawableState()const override;
void drawableStateChanged()override;
bool verifyDrawable(Drawable* who)const override;
public:
Switch(Context* context,const AttributeSet& attrs);
void setSwitchTextAppearance(Context* context,const std::string&resid);
void setSwitchTypeface(Typeface tf, int style);
void setSwitchTypeface(Typeface tf);
void setSwitchPadding(int pixels);
int getSwitchPadding()const;
void setSwitchMinWidth(int pixels);
int getSwitchMinWidth()const;
void setThumbTextPadding(int pixels);
int getThumbTextPadding()const;
void setTrackDrawable(Drawable* track);
void setTrackResource(const std::string& resId);
Drawable* getTrackDrawable();
void setTrackTintList(ColorStateList* tint);
ColorStateList* getTrackTintList();
void setTrackTintMode(PorterDuffMode tintMode);
PorterDuffMode getTrackTintMode()const;
void setThumbDrawable(Drawable* thumb);
void setThumbResource(const std::string& resId);
Drawable* getThumbDrawable();
void setThumbTintList(ColorStateList* tint);
ColorStateList* getThumbTintList()const;
void setThumbTintMode(PorterDuffMode tintMode);
PorterDuffMode getThumbTintMode()const;
void setSplitTrack(bool splitTrack);
bool getSplitTrack()const;
std::string getTextOn()const;
void setTextOn(const std::string&);
std::string getTextOff()const;
void setTextOff(const std::string&);
bool getShowText()const;
void setShowText(bool showText);
void onMeasure(int widthMeasureSpec, int heightMeasureSpec)override;
bool onTouchEvent(MotionEvent& ev)override;
void toggle()override;
std::string getButtonStateDescription()const;// override;
void setChecked(bool checked)override;
void draw(Canvas&)override;
int getCompoundPaddingLeft() ;//override;
int getCompoundPaddingRight();//override;
void drawableHotspotChanged(float x, float y)override;
void jumpDrawablesToCurrentState()override;
};
}//endof namespace
#endif

View File

@ -356,8 +356,8 @@ void ViewPager::setPageMarginDrawable(const std::string&resId){
setPageMarginDrawable(getContext()->getDrawable(resId));
}
bool ViewPager::verifyDrawable(Drawable& who) {
return ViewPager::verifyDrawable(who) || &who == mMarginDrawable;
bool ViewPager::verifyDrawable(Drawable* who)const{
return ViewGroup::verifyDrawable(who) || who == mMarginDrawable;
}
void ViewPager::drawableStateChanged(){

View File

@ -181,7 +181,7 @@ protected:
void setScrollState(int newState);
void setCurrentItemInternal(int item, bool smoothScroll, bool always,int velocity=0);
void drawableStateChanged()override;
bool verifyDrawable(Drawable& who);
bool verifyDrawable(Drawable* who)const override;
ItemInfo* addNewItem(int position, int index);
void dataSetChanged();
void populate();