I want to pass some data to a custom class that extends android.view.View.
However, I get a warning message saying that :
Custom view LinePlot is missing constructor used by tools: (Context)
or (Context,AttributeSet) or (Context,AttributeSet,int)
However, I run the code and all seems to work smoothly.
What is happening?
How should I pass my data to my class?
Why can't I use a custom constructor?
Thanks!
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.View;
import java.util.ArrayList;
public class LinePlot extends View {
private ArrayList<Float> mPoints;
private int dx;
private int dy;
Paint paint=new Paint();
public LinePlot(Context context,int dx_plot, int dy_plot, ArrayList<Float> points) {
super(context);
mPoints=points;
dx=dx_plot;
dy=dy_plot;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// plotting my data here
}
}
so a couple of steps have to followed here -
(i) in values/attrs.xml add the following code -
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="NinjaView">
<attr name="addButtonBackgroundDrawable" format="integer"/>
</declare-styleable>
</resources>
(ii) call your custom view in you layout.xml file. For e.g. -
<com.example.act.ui.utils.NinjaView
android:id="#+id/ninja_view_product_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:addButtonBackgroundDrawable="#drawable/add_button_back_about_product"/>
(Don't forget to declare your custom namespace : xmlns:custom="http://schemas.android.com/apk/res-auto")
(iii) In your custom view's class you'll have to add a couple of things. Example -
public class NinjaView extends LinearLayout {
#BindView(R.id.button_add_to_cart_product) Button mAddToCart;
public NinjaView(Context context) {
super(context);
initializeNinjaView(context);
}
public NinjaView(Context context, AttributeSet attrs) {
super(context, attrs);
initializeNinjaView(context);
setAddButtonBack(context, attrs);
}
public NinjaView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeNinjaView(context);
setAddButtonBack(context, attrs);
}
private void initializeNinjaView(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.ninja_view, this);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
}
private void setAddButtonBack(Context context, AttributeSet attrs) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.NinjaView, 0, 0);
Drawable backgroundDrawable;
try {
backgroundDrawable = typedArray.getDrawable(R.styleable.NinjaView_addButtonBackgroundDrawable);
} finally {
typedArray.recycle();
}
if (backgroundDrawable != null) {
(findViewById(R.id.button_add_to_cart_product)).setBackground(backgroundDrawable);
}
}
The warning means you won't be able to use your custom view in any xml layout.
If you are not intended to do that it's still good to implement those constructors for your custom view like this:
CustomView(Context ctx) {
super(ctx)
}
Any additional attributes are normally passed as custom attributes not as constructor parameters. Read on custom view attribytes in the docs or elsewhere http://www.tutorialspoint.com/android/create_custom_attributes_for_custom_component.htm
In your LinePlot class add constructors:
public LinePlot(Context context) {
super(context);
}
public LinePlot(Context context, AttributeSet attrs) {
super(context, attrs);
}
you need to add constructors with parameters
(Context), (Context,AttributeSet) and (Context,AttributeSet,int)
Related
I want to implement a custom ImageView with some predefined attributes based on the xml file. To do that I prepared a layout wrapped within merge tag:
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="#+id/my_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="center"
android:src="#drawable/icon" />
</merge>
And extended ImageView class:
public class CustomImageView extends LinearLayout{
public ImageFormField(Context context) {
super(context);
init();
}
public ImageFormField(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ImageFormField(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public ImageFormField(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
View.inflate(getContext(), R.layout.custom_image_view, this);
}
}
It works so far, but I actually don't need that LinearLayout as I could extend directly from the ImageView. By extending ImageView I'd like to have the possibility to override src parameter from the default layout.
So I removed merge tag to have only ImageView in the layout and tried this:
public class CustomImageView extends AppCompatImageView{
public ImageFormField(Context context) {
super(context);
init();
}
public ImageFormField(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public ImageFormField(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
View.inflate(getContext(), R.layout.custom_image_view, null); //can't pass root here
}
}
... but the image is simply not displayed. I want to be able to use my view this way:
<com.my.package.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
with possibility to override src attribute. Is there a way to do that by inflating layout or do I have to go deep with attributes (including custom ones)?
UPDATE
By "overriding src attribute I mean that by default image will have source from its xml file, but user can use it that way to pass another value within this custom view:
<com.my.package.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/another_icon" />
To provide a "default" image but also allow the user to override that default by specifying the android:src attribute, you can do this:
package com.example.stackoverflow;
import android.content.Context;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
public class CustomImageView extends AppCompatImageView {
public CustomImageView(Context context, AttributeSet attrs) {
super(context, attrs);
if (getDrawable() == null) {
setImageResource(R.drawable.default_image);
}
}
}
Then you can use it like this:
<!-- will display `default_image` -->
<com.example.stackoverflow.CustomImageView
android:layout_width="200dp"
android:layout_height="200dp"/>
Or:
<!-- will display `other_image` -->
<com.example.stackoverflow.CustomImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:src="#drawable/other_image"/>
There's no need to inflate anything inside the custom image view, and no need to create custom attributes (though you could certainly create custom attributes and use them if you wanted to).
You could update the Java code to apply other "default" styles too.
By default to change textColor programatically is :
textView.setTextColor(Color.RED);
I need to have a custom Textview to change typeface and color by default, How can change textcolor from CustomTextView class, here is my code.
public class CustomTextView extends TextView {
public CustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomTextView(Context context) {
super(context);
}
public void setTypeface(Typeface tf, int style) {
if(!isInEditMode()) {
if (style == Typeface.BOLD) {
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/Lato-Bold.ttf"));
} else if(style == Typeface.ITALIC){ // constant used to set Lato-Light.
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/Lato-Light.ttf"));
}else {
super.setTypeface(Typeface.createFromAsset(getContext().getAssets(), "fonts/Lato-Regular.ttf"));
}
}
}
The below code is the way to set your default text color and typeface.
public class CustomTextView extends TextView {
public CustomTextView(Context context) {
super(context);
init(context);
}
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setTypeface(Typeface.createFromAsset(context.getAssets(),"fonts/Lato-Light.ttf"));
setTextColor(Color.RED);
}
}
The init() method gets called every time the text view gets created, and will then set the typeface and color in that. You can manipulate any other variables you want to in there.
Use setTextColor(Color.RED); after each super in each constructor.
Step 1
In the /assets directory (not the /resource directory), create a folder called /fonts. Copy your custom font here. You can use both TTF and OTF fonts.
Step 2
In the /res/values folder, create a new file called attrs.xml. This is how the Android SDK lets you name custom properties for your widgets.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyTextView">
<attr name="fontName" format="string" />
</declare-styleable>
</resources>
Step 3
In /res/layouts, you will need to include your to-be-created custom text view in the activity_main.xml file.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:customfontdemo="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="24sp"
android:padding="12dp"
android:text="Standard Android Font" />
<com.authorwjf.customfontdemo.MyTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="32sp"
android:padding="12dp"
customfontdemo:fontName="pipe_dream.ttf"
android:text="Custom Android Font" />
</LinearLayout>
Step 4
In the /src folder, you will want to create your MyTextView class. It extends the standard text view, plucks the font name from the custom attribute, and applies the type face.
package com.authorwjf.customfontdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.widget.TextView;
public class MyTextView extends TextView {
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public MyTextView(Context context) {
super(context);
init(null);
}
private void init(AttributeSet attrs) {
setTextColor(Color.RED);
if (attrs!=null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MyTextView);
String fontName = a.getString(R.styleable.MyTextView_fontName);
if (fontName!=null) {
Typeface myTypeface = Typeface.createFromAsset(getContext().getAssets(), "fonts/"+fontName);
setTypeface(myTypeface);
}
a.recycle();
}
}
}
Step 5
Because the text view is now self-contained, you aren't required to make any modifications to our /src/MainAcitivity.java file.
package com.authorwjf.customfontdemo;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
I am building a custom View that requires as one of its Attributes a Class<> object to an entity. While I made it work programmatically by adding a Setter for it, I was wondering if there is any good way to allow adding it to the XML for the layout as well?
There does not appear to be a format option for a styleable with type "class". I could use a String, but then I'd have to gamble that the value is actually a valid Class and I'd lose type hinting, so it'd not be ideal.
Is there any good way to make this work, or should I just stick with setting it programmatically?
Method 1 (With warnings):
Generic CustomView:
public class CustomView<T> extends View {
private List<T> typedList = new ArrayList<T>();
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void addTypedValue(T object){
typedList.add(object);
}
public T getTypedValue(int position){
return typedList.get(position);
}
}
Activity:
//unsafe cast!
CustomView<String> customViewGeneric = (CustomView<String>) findViewById(R.id.customView);
customViewGeneric.addTypedValue("Test");
String test = customViewGeneric.getTypedValue(0);
XML:
<org.neotech.test.CustomView
android:id="#+id/customView"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
Method 2 (No warnings, safe!):
This method uses a generic CustomView. And for each type that will be used in xml you will need to create a specific class.
I have added an example implementation:
Generic CustomView: (Do not inflate this one in xml):
public class CustomView<T> extends View {
private List<T> typedList = new ArrayList<T>();
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void addTypedValue(T object){
typedList.add(object);
}
public T getTypedValue(int position){
return typedList.get(position);
}
}
XML inflatable view for the String type:
public class CustomViewString extends CustomView<String> {
//ADD Constructors!
}
XML inflatable view for the Integer type:
public class CustomViewInteger extends CustomView<Integer> {
//ADD Constructors!
}
Activity:
CustomViewString customViewString = (CustomViewString) findViewById(R.id.customViewString);
CustomView<String> customViewGeneric = customViewString;
XML:
<org.neotech.test.CustomViewString
android:id="#+id/customViewString"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<org.neotech.test.CustomViewInteger
android:id="#+id/customViewInteger"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
I am creating my own layout based on RelativeLayout as a class in code
I have basics of the layout defined in XML R.layout.menu_layout (style, drawable for background, margin, height)
If I would not need a class then I would call inflater to do this:
RelativeLayout menuLayout = (RelativeLayout)inflater.inflate(R.layout.menu_layout, root);
But I would like to be calling my own class instead
MenuLayout menuLayout = new MenuLayout(myparams);
Since I need to create a class I need to somehow inherit the R.layout.menu_layout in constructor, how can I do that? I guess there is no this.setLayout(res); or this.setResource(res); in View. Maybe I can use the other two parameters in View constructor but I did not find any tutorial how to do that either.
public class MenuLayout extends RelativeLayout {
public MenuLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView(context);
}
public MenuLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
public MenuLayout(Context context) {
super(context);
initView(context);
}
private void initView(Context context) {
View view = LayoutInflater.from(context).inflate(R.layout.menu_layout, null);
addView(view);
}
}
now you can use
MenuLayout menuLayout = new MenuLayout(myparams);
you can change constructors for params i think
My custom view has dynamic custom attribute,e.g. the backgroundimage attribute,assign via current week.
I wan't to use construtor CalendarView(Context context, AttributeSet attrs) to pass several attribute,and I try to instantiate the attributeset with Xml.asAttributeSet,but it can't work. can anyone tell me how to do.
Note:my custom view has dynamic attribute ,so I don't wan't to instantiate the custom view via xml layout. my solution is incorrect ?
here is the custom view:
public class CalendarView extends View {
int backgroundImage;
public CalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
backgroundImage = attrs.getAttributeResourceValue("http://www.mynamespace.com", "backgroundimage", 0);
}
}
here is the activity:
public class TestActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(createTestView(0));
}
public CalendarView createTestView(int currentWeek) throws XmlPullParserException, IOException {
String attributes = "<attribute xmlns:android=\"http://schemas.android.com/apk/res/android\" " +
"xmlns:test=\"http://www.mynamespace.com\" " +
"android:layout_width=\"fill_parent\" android:layout_height=\"30\" " +
"test:backgroundimage=\"#drawable/"+ currentWeek +"_bg" + "\"/>";
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(new StringReader(attributes));
parser.next();
AttributeSet attrs = Xml.asAttributeSet(parser);
return new CalendarView(this,attrs);
}
}
You have to declare the attribute as styleable on attr.xml file located in values the
for example:
<declare-styleable name="yourView">
<attr name="aAttr" format="dimension" />
<attr name="bAttr" format="dimension" />
>
After that you have to use them as this in your custom view, and you must declare both default constructors:
public CalendarView(Context context) {
super(context);
}
public CalendarView(Context context, AttributeSet attrs) {
super(context, attrs);
backgroundImage = attrs.getAttributeResourceValue("http://www.mynamespace.com",
"backgroundimage", 0);
int typefaceIndex = attrs.getAttributeIntValue("http://schemas.android.com/apk/res/android", "typeface", 0);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.yourView);
}
This should work, to get parameters for your custom view. Feel free to ask if you don't undersand.
the typeFaceindex is just an example which works.
After that you have to use your customView into layout like any other this:
<com.example.yourView> </com.example.yourView>
Maybe I don't get what you are doing or why you're doing it, but I find your attempt with the AttributeSet quite complicated.
I'd suggest you create another constructor like this
public CalendarView(Context context, int backgroundRessourceID) {
super(context);
setBackgroundResource(backgroundRessourceID);
}
And then instantiate your CalendarView like this
CalendarView cv = new CalendarView(this, R.drawable.0_bg);
cv.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 30));
Hopefully this solved your issue ...