mirror of
https://gitee.com/houstudio/Cdroid.git
synced 2024-11-29 18:59:14 +08:00
add switch(a button with on/off animation)
This commit is contained in:
parent
67538a268b
commit
842bf7cb70
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
22
src/gui/core/mathutils.h
Executable 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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
842
src/gui/widget/switch.cc
Executable 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
153
src/gui/widget/switch.h
Executable 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
|
@ -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(){
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user