There’s a lot of stuff on the Internet about how to write shapes and selectors in.xml, so why write another one? Recently in the study of AOP, THINKING whether AOP can be used to achieve the way, so there is this article. The main purpose is to provide a different way to implement, but also for students learning AOP some reference.
rendering
BgDrawable.gif
Method of use
Add it in the build.grade file of the main Module
apply plugin: 'android-aspectjx' dependencies { ... Implementation 'com. Stubborn: bgdrawable: 1.0.0'}Copy the code
Add it to the build.grade file in the project root directory
dependencies { ... The classpath 'com. Hujiang. Aspectjx: gradle - android plugin - aspectjx: 2.0.2'}Copy the code
In an Activity or Fragment, add @bgDrawable to the control as shown in the following example:
@BgDrawable(id = R.id.button,
color = R.color.colorPrimary,
cornerRadius = 20,
strokeWidth = 3,
strokeColor = R.color.colorAccent,
pressedColor = R.color.colorAccent
)
private Button button;
Copy the code
Note that the id here is the id of the control. Make sure that the sum is correct, otherwise it will have no effect. Properties such as strokeWidth must be in dp, and properties related to color must be in r.color. XXX form.
The following attributes are supported:
int id() default 0;
int shape() default GradientDrawable.RECTANGLE;
int color() default 0;
int alpha() default 255;
float cornerRadius() default 0;
float topleftRadius() default 0;
float toprightRadius() default 0;
float bottomleftRadius() default 0;
float bottomrightRadius() default 0;
float width() default 0;
float height() default 0;
float strokeWidth() default 0;
int strokeColor() default 0;
float dashWidth() default 0;
float dashGap() default 0;
GradientDrawable.Orientation orientation() default GradientDrawable.Orientation.TOP_BOTTOM;
int startColor() default 0;
int centerColor() default 0;
int endColor() default 0;
int gradientType() default GradientDrawable.LINEAR_GRADIENT;
float gradientCenterX() default 0;
float gradientCenterY() default 0;
float gradientRadius() default 0;
int pressedColor() default 0;
int selectedColor() default 0;
int checkedColor() default 0;
int focusedColor() default 0;
int unabledColor() default 0;
int pressedStrokeColor() default 0;
int selecteStrokedColor() default 0;
int checkedStrokeColor() default 0;
int focusedStrokeColor() default 0;
int unabledStrokeColor() default 0;
Copy the code
It’s pretty easy to just add these properties to @bgDrawable to implement shape and selector.
implementation
For those not familiar with AOP, check out these two articles: AOP AspectJ’s comprehensive anatomy of Aspect Programming in Android: An introduction to AOP and Aspects
Take the Activity as an example, the code is as follows:
public class MainActivity extends AppCompatActivity { @BgDrawable(id = R.id.button, color = R.color.colorPrimary, cornerRadius = 20, strokeWidth = 3, strokeColor = R.color.colorAccent, pressedColor = R.color.colorAccent, ) private Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = findViewById(R.id.button); }}Copy the code
Normally in an Activity, to get a control we need to call the Activity’s findViewById method, so we can use AspectJ to inject code into the findViewById method. The code is as follows:
@Aspect public class BgDrawableAspect { @Around("call(* android.app.Activity.findViewById(..) )") public Object executionfindViewById(ProceedingJoinPoint joinPoint) throws Throwable { Object returnValue = joinPoint.proceed(); //findViewById return value Field[] fields = joinPoint.getThis().getClass().getDeclaredFields(); //MainActivity member variable for(Field Field: fields){field.setaccessible (true); BgDrawable anno = field.getAnnotation(BgDrawable.class); // get @bgDrawable if(anno! =null && returnValue! =null) { View view = (View) returnValue; if(view.getId()==anno.id()) { Drawable drawable = BgDrawableUtil.getDrawable(view.getContext(), anno); view.setBackground(drawable); break; } } } return returnValue; }}Copy the code
In the above code, we use JoinPoint.proceed () to get the return value of findViewById, which is our target control. Next, we need to get the MainActivity member variable from it, which has the @BGDrawable annotation. If the ID in the annotation is the same as the ID of the target control, the set attribute is obtained from the @BGDrawable annotation to create a Drawable object, and the Drawable is set to the background of the target control. Above is the Activity in the implementation process, implementation process in the fragments are roughly the same, just change to injection method to view the findViewById method namely android. View. The view. The findViewById (..) . One might ask, what if I use Butterknife instead of the findViewById method? Is it still supported? Please read on.
Support for Butterknife
Here is an example of an Activity using Butterknife:
public class ButterknifeActivity extends AppCompatActivity { @BgDrawable(id = R.id.button, color = R.color.colorAccent, cornerRadius = 10, pressedColor = R.color.colorPrimary ) @BindView(R.id.button) Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_butterknife); ButterKnife.bind(this); }}Copy the code
Using the ButterknifeActivity class for Butterknife, a.class file called ButterknifeActivity_ViewBinding is generated at compile time.
public class ButterknifeActivity_ViewBinding implements Unbinder { private ButterknifeActivity target; @UiThread public ButterknifeActivity_ViewBinding(ButterknifeActivity target) { this(target, target.getWindow().getDecorView()); } @UiThread public ButterknifeActivity_ViewBinding(ButterknifeActivity target, View source) { this.target = target; target.button = (Button)Utils.findRequiredViewAsType(source, 2131165218, "field 'button'", Button.class); } @CallSuper public void unbind() { ButterknifeActivity target = this.target; if (target == null) { throw new IllegalStateException("Bindings already cleared."); } else { this.target = null; target.button = null; }}}Copy the code
We’ll look at the structure of the ButterknifeActivity_ViewBinding method, the parameters of the target is ButterknifeActivity instance objects, Utils. FindRequiredViewAsType method can get to our goal control, It is also internally implemented through the View’s findViewById method, which is ultimately assigned to target.button, the activity’s member variable button. Given the Activity instance object and the target control, if we inject code into this constructor, we can do what we want.
@Aspect public class BgDrawableAspect { @After("initialization(* .. *_ViewBinding.new(..) )") public void executionbButterknife(JoinPoint joinPoint) throws Throwable { Object target = joinPoint.getArgs()[0]; Field[] fields = target.getClass().getDeclaredFields(); Field[] fields = target.getClass(). // Get the Activity's member variable for(Field Field: fields){field.setaccessible (true); Object fieldValue = field.get(target); BgDrawable anno = field.getAnnotation(BgDrawable.class); if (anno ! = null && fieldValue instanceof View) { View view = (View) fieldValue; if (view.getId() == anno.id()) { Drawable drawable = BgDrawableUtil.getDrawable(view.getContext(), anno); view.setBackground(drawable); } } } } }Copy the code
In this code, the constructor is injected After the initialization, and the @After method is injected After the original method. The activity member variable target.button is already assigned as the target control. Create a Drawable object based on the @bgDrawable annotation and set the Drawable to the background of target.button.
See the source code for specific implementation
Welcome to.jpg