custom view with extra string parameters in constructor - android

How can I passs multiple strings into custom views constructor in android
public DrawSomethingView(Context context, String originalBgPath, String pattern) {}
and why it forces to do it like that ?
public DrawSomethingView(Context context, AttributeSet S){

You need the standard constructors so the framework knows how to inflate them, e.g. from an XML layout. If you put a DrawSomethingView in your layout, how would it know what to pass for originalBgPath etc? You also need a Context and AttributeSet at a minimum to call one of the View superclass's constructors. You can read more about it here
That link also tells you how to create your own attributes, that you can add to the XML for your custom view, and pull out the data in the class itself. So you could add your strings as part of the XML definition. If you want to set them programmatically instead, you'll need to add some properties you can set (which you could also set when you read those attributes).
But there's no way to force them to be set, since you can't create your own version of the constructor that requires them (and the framework wouldn't have values to provide anyway). So you'll need to handle the possibility they're missing (e.g. nullable properties, default values), and maybe write a builder function that you can call from your code, that does require those values and creates and sets up the custom view before passing it back to you.

Related

How to pass AttributeSet as a parameter

I'm trying to implement updated solution by Pedram from this answer, but I don't know to create a new instance of CircleProgressBar. It requires AttributeSet to be passed as a parameter, but how to get it?
CircleProgressBar circleProgressBar = new CircleProgressBar(MainActivity.this, ?);
The AttributeSet constructor is used when a view is inflated through a layout defined in XML. If you're constructing one in code, you should use the single argument constructor (e.g. new CircleProgressBar(MainActivity.this)). If the single-argument constructor is not defined, you just need to add it. You'll just need to add some getters/setters for the properties if you want to be able to construct it entirely from within Java code.
Alternately, just define a layout XML (example name view_progress_circle.xml) with a single item:
<com.your.packagename.CircleProgressBar
android:layout_width="100dp"
android:layout_height="100dp"
// etc. add other attributes here
/>
Then in code, create it with:
CircleProgressBar bar = (CircleProgressBar) LayoutInflater.from(MainActivity.this)
.inflate(R.layout.view_progress_circle, parent, false):
where parent is the ViewGroup you're going to attach the view to.

Passing values to custom view in android

Cheers,
I have an app that receives user input (2 numbers, width and height) and in theory depending on that input I have a custom view that should draw a grid (width and height).
Note:
These 2 values should be received before view attempts to draw itself.
These 2 values aren't constant and therefore I don't think XML approach can help.
I was told that adding another parameter to the View constructor is evil.
Do not confuse my 2 values with canvas.getWidth or etc.. these are values needed simply to draw something, nothing else.
My View is also a ViewGroup.
Main issue arises with Views declared in XML files.
I have temporarily solved this issue by making an SchemeContext class which contains those 2 static values and I simply set them in onCreate (before onCreateView) then use them in custom View onDraw when needed (SchemeContext.width). This is not really what people would call OOP I'm forcing global variables upon java and those are set on time because of the fragment lifecycle.
I've seen this answer How to pass variables to custom View before onDraw() is called?.
But it's more of a workaround than a solution (and probably not the fastest one). There has to be a sensible solution I don't think 3D games on android resort to these workarounds (SurfaceView with OpenGL is still a View right? :d).
If there is an obvious solution and this is an obvious double I'll remove the question.
I haven't tried this, but I think it would be possible to do this fairly cleanly by overriding the LayoutInflater.Factory. That way, you can intercept the creation of the views that need additional parameters passed to their constructors, and let the rest of them fall through to default inflation.
For example, in your activity, before you inflate the view hierarchy:
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
MyInflaterFactory factory = new MyInflaterFactory();
// Pass information needed for custom view inflation to factory.
factory.setCustomValue(42);
inflater.setFactory(factory);
For your implementation of the factory:
class MyInflaterFactory implements LayoutInflater.Factory {
public void setCustomValue(int val) {
mCustomVal = val;
}
#Override
public View onCreateView (String name, Context context, AttributeSet attrs) {
if (name.equals("com.package.ViewWithCustomCreation")) {
return new ViewWithCustomCreation(context, attrs, mCustomVal);
}
return null;
}
private int mCustomVal;
}
I was told that adding another parameter to the View constructor is evil.
Nonsense.
There are three (and in the newest APIs, four) different View constructors, each used in a different situation. (See this thread.) If you wanted to be able to declare your view in XML, for example, then you'd have to provide a constructor with exactly the right parameters, and have it call the corresponding superclass constructor. But there's nothing wrong with defining your own constructor (or even several of them) that call the superclass constructor intended for creating views programmatically.
The overriding principle is that every object must be valid when its constructor returns. So unless you can provide reasonable default values in your constructor, you have little choice but to accept the object's properties as constructor parameters.

How can I add multiple tags for one view directly in XML?

As the question states I simply want to add more than one tag to an XML View. For example, say I want to set an array of strings AND a separate string from my resources. I know how to do them individually but I want to know if there's a way of attaching more than one tag to a view directly within the XML code.
Edit:
My plan was to have a LinearLayout (l#1) that contained a dynamic amount of of a different LinearLayout (l#2) and within that View there would be a Spinner and an EditText. I need one tag for the hint of the EditText and the other for the array of strings to populate the Spinner. In the entire layout there are a multiple l#1 each using l#2 to populate it dynamically and each needing different hints and string arrays based on what they are used for.
My next idea was to add a integer as a tag to represent l#1 and and use a Switch/Case block in my code to populate the children of l#2 with the right hints and string arrays.
I don't think this is possible in XML, but in code what you could do is create a custom object which holds the strings you require and set that as the tag.
class CustomTagObject {
public List<Strings> strings;
public String myString;
}
Then later
CustomTagObject tagObj = new CustomTagObject();
tagObj.strings = new ArrayList<Strings>("String 1", "String 2");
tagObj.myString = "String from resources";
view.setTag(tagObj);
If you explain why you want to hold these items as the tag, I may be able to help you find an alternative approach?
Above solution works, but the usage is wrong(it will add extra overhead on your end to manage the key/value map).
The better way to achieve above is to use an overloaded method of setTag which allows you to specify id associated with the value.
Method signature:
public void setTag(int key, Object tag)

android getting variables to constructor of custom view

android newbie here.
My first game involves a custom view, which is going to draw a game board and some scoreboards on the screen. I need to know how many players there are in order to get the number of scoreboards up, and I need this to be in known in the constructor of the custom view, so that appropriate variables are initialised on time.
My current implementation is like this, is this the correct way to get variables into the custom view constructor?...
I instantiate the custom view from my activity like this:
numPlayers=2;
setContentView(R.layout.gamescreen);
mBoardView = (BoardView) findViewById(R.id.board_view);
And in the custom view constructor:
public BoardView(Context context, AttributeSet attrs){
super(context, attrs);
vNumPlayers = ((GuappsXOMainGame)getContext()).getNumP();
That's how I have it now and it seems to work well enough, but is it better to be doing something along the lines of the answer to this question:?
android:how to instantiate my custom view with attributeset constructor
When a custom view (or SurfaceView) is the only view, often in games, then it is very easy to pass parameters like level, players, sound on etc by creating an instance of this view before setContentView(). An example:
MyView myView = new MyView(level, players, soundOn);
setContentView(myView);
In your case, your custom view is instantiated when the layout is created, hence you have to communicate back to the activity object to get the values. This is also possible. I have done this by setting static variables in the activity before calling setContentView(my_layout) then in the constructor for the custom view just say level = MyActivity.level
Or perhaps the way you are doing already where you obtain the instance of the activity and call a public method or variable.
In the link you provided an attribute is "hard-wired" into the XML layout. I don't see the advantage of that, when one can simply enter the value into the custom view class itself as a "final" variable.

How to set Custom EditTextField properties in android?

I am new to android .can any one solve the following problem?
I just create the class like below .I need to know how to set property for the Edit text field
public class CustomEditText extends EditText{
public CustomEditText(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
}
Note: I mean the property like this
Edittext.setText("Demo");
Thanks in advance.
You need to create member variable and methods inside your CustomEditText.
Once you have them you can access it.
So there are a couple ways this can be interpreted and I will try to cover all of them.
EditText has multiple constructors. The one you have overridden requires that you as the developer set the properties in code for the rest of this instances usage. So you can actually just call setText(someString) from within this class or since the method is public call it directly on an instance of your class.
If you override the constructor that contains an attributeSet,
EditText(Context, AttributeSet)
You can use your component as part of an xml layout and set attributes on it there as if it were another EditText (as long as you call super(context, attributeSet). If you want to define your own custom attributes on top of that then that's actually quite neat how you do this.
In your project hierarchy, from the root you should either have or need to create a folder called "res/values" Within that folder a file named attr.xml should be created.
<declare-styleable name="myCustomComponents">
<!-- Our Custom variable, optionally we can specify that it is of integer type -->
<attr name="myCustomAttribute" format="integer"/>
...
<!-- as many as you want -->
</declare-styleable>
Now within your new constructor that makes use of the AttributeSet, you can read this new attribute, "myCustomAttribute".
public CustomEditText(Context context, AttributeSet set) {
super(context, set);
// This parses the attributeSet and filters out to the stuff we care about
TypedArray typedArray = context.obtainStyledAttributes(R.stylable.myCustomComponents);
// Now we read let's say an int into our custom member variable.
int attr = typedArray.getInt(R.styleable.myCustomComponents_myCustomAttribute, 0);
// Make a public mutator so that others can set this attribute programatically
setCustomAttribute(attr);
// Remember to recycle the TypedArray (saved memory)
typedArray.recycle();
}
Now that we have declared our new attribute and have setup code to read it, we can actually use it programatically or in an XML layout. So let's say you have an Xml layout in file, layout.xml
<ViewGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.my.apk.package"
>
...
<com.my.full.apk.package.CustomEditText
android:id="#+id/custom_edit"
...
custom:myCustomAttribute="12"
/>
...
</ViewGroup>
So in this we create a layout like normal, but notice we declare a new xml namespace. This is for your new component and your apk. So in this case "custom" is being used it will look inside your defined stylables for new parameters. By doing the previous steps with attr.xml, you have declared "myCustomAttribute" as a component attribute off of the http://schemas.android.com/apk/res/com.my.apk.package namespace. After that it's up to you to decide what attributes you would like to expose and what those attributes actually mean. Hope that helps.

Categories

Resources