I am currently writing a custom ImageView. what im actually turning this imageview into is a multiple state button.
In my case, the button has three states, answered with yes, answered with no and unanswered.
I strictly don't want to use a radiogroup/button.
I have the basic setup for the imageview. It has onclick functionality to properly change the image depending on the state. Which for now is just a checkbox with a cross, an empty checkbox and a checkbox with a tick.
What I want is, when the state changes(the image is clicked) i want text, which should Always be around, and never ontop of the imageview. To change its value aswell. I want to be able to call a method similar to customCompInstance.getText(); customCompInstance.getText();
What im thinking is, the custom imageview class should have a TextView member, but I have NO clue whatsoever how to place it on the left, horizontally aligned to the imageview.
It is NOT an option to just use two different elements in xml.
Is adding a textview and placing it to the right of the imageview the right solution, if not, what do you suggest, if so, please give me some tips on how to actually achieve this.
Simplified version of my class:
public class AnswerImageView extends ImageView {
private AnswerState mSelectionState;
/**
* #param context
*/
public AnswerImageView(Context context)
{
super(context);
// TODO Auto-generated constructor stub
setImageResource(R.drawable.answer_none);
setTag(R.drawable.answer_none);
mSelectionState = AnswerState.ANSWER_NONE;
}
/**
* #param context
* #param attrs
*/
public AnswerImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
// TODO Auto-generated constructor stub
setImageResource(R.drawable.answer_none);
setTag(R.drawable.answer_none);
mSelectionState = AnswerState.ANSWER_NONE;
}
/**
* #param context
* #param attrs
* #param defStyle
*/
public AnswerImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
setImageResource(R.drawable.answer_none);
setTag(R.drawable.answer_none);
mSelectionState = AnswerState.ANSWER_NONE;
}
public AnswerState getSelectionState() {
return mSelectionState;
}
public void setSelectionState(AnswerState selectionState) {
this.mSelectionState = selectionState;
}
}
I have a method that reacts to onclicks, that all works fine. im purely asking about adding a textview in this component. Essentially combinding two components into one.
You should extend from TextView instead of ImageView and have a look at methods TextView.setCompoundDrawableXXX. Here is the link for the doc.
Related
I am in the process of making a custom view that is essentially an ImageButton with added logic so it also have the behavior of a RadioButton. All I want to do is have it built into the view that when the user clicks the button the image is changed, an internal boolean is marked true to note it is selected, and an interface method is called to let the RadioGroup it is a part of to unselect all the other views within it. I don't want to impact the existing behavior of the base ImageButton whatsoever.
I've only made one other custom view before and that was by following a tutorial almost exactly to the letter and since there are so many different methods inhereted from View that deal with clicks/touches (i.e. onTouch, onClick, motion event, etc.) taking it all in has left me a bit confused. I am fine writing the interface itself, its the modification of ImageButton where I'm not too sure how to attack it.
So, I ask you all: What method/methods do I need to override to add this simple functionality, while not impacting the current behavior of ImageButton, nor screwing up the ability to set an onTouchListener for the button that will perform additional actions on click without compromising this built in radio button logic? If I need to override something that will mess with the default behavior I mentioned, what do I need to put in the new method to restore that functionality?
This is what I have so far:
public class RadioImageButton extends AppCompatImageButton implements RadioCheckable {
//Default constructor
public RadioImageButton(Context context) {
super(context);
initView();
}
//Constructor with defined attributes
public RadioImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes();
initView();
}
//Constructor with defined attributes and attributes taken from style defaults that aren't defined
public RadioImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//=========================================================================
// Setup
//=========================================================================
private void initView()
{
}
private void parseAttributes()
{
}
}
The approach I would like to take would be something like:
...All other code I already showed
mChecked = false;
#Overide
void onClick(...)
{
mChecked = true;
setImageSource(R.example.checked_image); // Or I can use a selector resource
*Call to Radio Interface*;
mOnTouchListener.onTouch(v, event); //Handle user onTouchListener
}
...
and leave all the other code alone, though I'm sure it isn't quite that simple.
I thought a good start would be trying to find the source code for the default ImageButton class and set mine up to be a near replica so I can understand how it works and then modify from there, but all I could really find was this:
https://android.googlesource.com/platform/frameworks/base/+/android-7.0.0_r35/core/java/android/widget/ImageButton.java
and there is no way that is the actual source because pressing Ctrl+O shows many more functions that ImageButton defines that are not inherited from another class; regardless, that link is not at all helpful as its basically a giant comment with little to no code.
Thanks for any suggestions that will help me accomplish this in the most straightforward way.
EDIT: #pskink - Looking through the code you provided, it seems like it is trying to generate a matrix in order to transform the provided drawable (src) so that it fits into a new rectangle (dst) while maintaining the aspect ratio and positioning (hence ScaleToFit.CENTER). I would assume the destination rectangle would be the bounds of the view the drawable is contained in, which in this case is the RadioButton, but while stepping through the override of the "draw()" method it doesn't quite seem to be doing that, though I'm not quite sure how cavas.concat(matrix) is resolved so I'm not positive. Regardless it doesn't seem to work as intended or I am somehow using it wrong.
While maybe not the most robust method, it seems like the most straightforward, yet effective way to handle what I wanted to do was to leverage the Matrix class and its powerful scaling/transformation tools, specifically "setRectToRect()". Creating a custom view that extends RadioButton instead of ImageButton allowed me to make use of the existing RadioGroup, while manipulating characteristics of the button's drawables in the new classes Constructor achieved the behavior I was looking for.
Custom RadioButton class:
public class RadioImageButton extends android.support.v7.widget.AppCompatRadioButton {
int stateDrawable; //Resource ID for RadioButton selector Drawable
D scaledDrawable; //Post-scaling drawable
public RadioImageButtonTwo(Context context) {
super(context);
initView();
}
public RadioImageButtonTwo(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(attrs);
initView();
}
private void parseAttributes(AttributeSet attrs)
{
TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs,R.styleable.RadioImageButtonTwo);
try {
// Obtain selector drawable from attributes
stateDrawable = styledAttrs.getResourceId(R.styleable.RadioImageButtonTwo_button_sDrawable, R.drawable.test_draw2);
} finally {
styledAttrs.recycle(); //Required for public shared view
}
}
private void initView()
{
scaledDrawable = new D(getResources(),stateDrawable); // Create scaled drawable
setBackground(scaledDrawable); // Apply scaled drawable
setButtonDrawable(android.R.color.transparent); // "Disable" button graphic
}
}
See more on setting up a custom view here: https://developer.android.com/training/custom-views/create-view#customattr
Custom drawable class "D" that includes fitCenter scaling thanks to #pskink:
class D extends StateListDrawable {
private Rect bounds = new Rect();
private RectF src = new RectF();
private RectF dst = new RectF();
private Matrix matrix = new Matrix();
public D(Resources r, int resId) {
try {
XmlResourceParser parser = r.getXml(resId);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG && parser.getName().equals("selector")) {
inflate(r, parser, Xml.asAttributeSet(parser));
break;
}
}
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
}
}
#Override
public void draw(Canvas canvas) {
Drawable current = getCurrent();
bounds.set(0, 0, current.getIntrinsicWidth(), current.getIntrinsicHeight());
current.setBounds(bounds);
src.set(bounds);
dst.set(getBounds());
matrix.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER);
canvas.concat(matrix);
super.draw(canvas);
}
}
Note that for whatever reason setting the button drawable itself to this custom drawable breaks the scaling, so changing the background to the custom drawable and setting the button drawable to transparent was the only way this worked. This custom drawable could easily be expanded upon to have more scaling type options and another view attribute could be defined to allow the user to choose the scaling type through XML.
This custom ImageView that mimics the (pointed out by pskink aswell) could also prove helpful in this task, as it too utilizes the Matrix class to implement multiple types of image scaling: https://github.com/yqritc/Android-ScalableImageView
I have a Scrollview and I have the attribute android:clickable="true" and android:autoLink="all".
I have a string for the ScrollView, and the emails, tel numbers etc, appear and are correctly clickable.
However, The string contains other numbers, such as Years, which also appear clickable and I don't want this; how can I stop this from happening?
Don't use autoLink="all", use the ones you need.
android:autoLink="web|email|phone" will probably cover your use cases.
The clickable="true" on the ScrollView isn't needed for this; rather you should set the autoLink attribute on the TextViews themselves; perhaps extracting a style if you have other common properties.
Add the new Linkify class to your project. From a place that you have access to the TextView (e.g. the Activity):
TextView myTextView = // get a reference to your textview
int mask = Linkify.ALL;
Linkify.addLinks(myTextView, mask);
The addLinks(TextView, int) method is static, so you can use it without creating an instance of Linkify. The return value (boolean) indicates whether something was linkified, but you probably don't need this information, so we don't bother with it.
You'll need to ensure that you don't put the autoLink attribute on the TextViews, otherwise the setText(...) implementations will still linkify years (unless you completely override the setText(...) implementations without calling super.setText(...))
For extra brownie points, you can create a subclass of TextView which will do the linkify for you when you set text on it:
public class AutoLinkifyTextView extends TextView {
public AutoLinkifyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoLinkifyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void setText(String text) {
super.setText(text);
parseLinks();
}
#Override
public void setText(int stringRes) {
super.setText(stringRes);
parseLinks();
}
private void parseLinks() {
Linkify.addLinks(this, Linkify.ALL);
}
}
For top marks of course, you'd read the attributes from the attrs and use the correct mask from the XML attributes, but I'd prefer to get rid of that option and do it here.
How do I set up a constructor in a custom TextView to be able to pass text from a fragment?
In other words, I'm confused how to send text from my fragment (Fragment1) to the custom view (View1):
public class View1 extends TextView {
//constructors:
public View1(Context context, AttributeSet ats, int ds) {
super(context, ats, ds);
init();
}
public View1(Context context) {
super(context);
init();
}
public View1(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
...
canvas.drawText(myString, margin1, margin2, paint); //myString is from Fragment1
....
}
I asked a similar question here, but didn't really get much help. Example code would go a long way towards clearing up my confusion. Thanks in advance!
You are extending a TextView anyway. As A--C mentioned, you can use getText(), as well as setText() to get and set the text.
In your context, I am not sure if it is a good idea to use TextView to implement your custom view/widget. View might be a better starting point, as TextView carries all kind of stuff around for formatting, icon/drawable display, click/button logic etc.
You need to define the standard constructors if you want to be able to have the system instantiate/inflate your components from an XML layout. Then you can use standard getters/setters for your data, same way as all other controls do it.
If you instantiate your widget/view yourself (in your code), you are free to define whatever constructors you want to (I believe).
I want to change imageview's image when user is holding button and change the image back to the default when user releases button.
Use OnFocusChangeListener(view) method & override OnFocusChange() method to this.
You need to use XML Selectors for this. You can specify whatever drawable you want for whatever state you want.
The docs are here: http://developer.android.com/reference/android/graphics/drawable/StateListDrawable.html
You will create an XML, that has references to each of the states you want to apply styles to, then you will reference when you assign a drawable to a view (a button for instance), instead of a particular drawable.
Check this SO question for more details: Android selector & text color
You can set an OnTouchListener to the ImageView and in the OnTouchListener you can set the image you want to display in MotionEvent.ACTION_DOWN and revert the old image in MotionEvent.ACTION_UP.
You can create a custom Button class like this
public class MButton extends Button {
public MButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public MButton(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public MButton(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
// define style
/**
* Send resource ids for normal, selected, pressed in respective order
*
* #param mImageIds
*/
public void setDrawablePressedBg(Integer... mImageIds) {
StateListDrawable bg = new StateListDrawable();
Drawable normal = this.getResources().getDrawable(mImageIds[0]);
Drawable selected = this.getResources().getDrawable(mImageIds[1]);
Drawable pressed = this.getResources().getDrawable(mImageIds[2]);
bg.addState(View.PRESSED_ENABLED_STATE_SET, pressed);
bg.addState(View.ENABLED_FOCUSED_STATE_SET, selected);
bg.addState(View.ENABLED_STATE_SET, normal);
bg.addState(View.FOCUSED_STATE_SET, selected);
bg.addState(View.EMPTY_STATE_SET, normal);
this.setBackgroundDrawable(bg);
}
// define style
public void setBackgroundPressedBg(Integer p1, Integer p2, Integer p3) {
StateListDrawable bg = new StateListDrawable();
Drawable normal = this.getResources().getDrawable(p1);
Drawable selected = this.getResources().getDrawable(p2);
Drawable pressed = this.getResources().getDrawable(p3);
bg.addState(View.PRESSED_ENABLED_STATE_SET, pressed);
bg.addState(View.ENABLED_FOCUSED_STATE_SET, selected);
bg.addState(View.ENABLED_STATE_SET, normal);
bg.addState(View.FOCUSED_STATE_SET, selected);
bg.addState(View.EMPTY_STATE_SET, normal);
this.setBackgroundDrawable(bg);
}
}
And you can use it like this in your onCreate Method
this.book_appointment = (MButton) this._view
.findViewById(R.id.btn_book);
this.book_appointment.setBackgroundPressedBg(R.drawable.btnnormal,
R.drawable.btnsel, R.drawable.btnsel);
This will go like this in your xml layout file
<com.packageName.custom.MButton
android:id="#+id/btn_book"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/tv_time1"
android:layout_marginTop="20dp"
android:background="#drawable/bookapointment" />
here in this post you can find the solution:
https://stackoverflow.com/a/18196156/2000517
Take care! ;)
I am doing a custom editbox class. It starts something like:
public class AmountField extends EditText {
and has overriden the constructor to implement the EditText widget style
/**
* Make a new AmountField Object.
*
* #param context
* the context of the field
* #param attrs
* attributes for the view
*/
public AmountField(Context context, AttributeSet attrs) {
this(context, attrs, R.style.Widget_EditText);
}
However when I implement this on my XML I get an editable TextView instead of the EditText (there is only text but not the surrounding white box) and also when I click on it nothing happens, only text color changes.
Are there any ideas on why this happens? I've done list components the same way and style is not altered.
Thanks in advance
Forget it, i have just realized that the constructor must call super instead of other constructor with more parameters.