Accepting a text style for a custom view - android

So I am writing my own custom view, that has both a TextView and an EditText within it. What I am trying to do is let the user set a text style for each one individually, like this:
<declare-styleable name="InputRow">
<attr name="descriptionTextStyle" format="string" />
<attr name="valueTextStyle" format="string" />
</declare-styleable>
I thought this would work, so that in XML I could say:
app:descriptionTextStyle="bold"
app:valueTextStyle="italic"
However, the problem comes when I am trying to read from the typed array. I can get the string:
if(typedArray.hasValue(R.styleable.InputRow_descriptionTextStyle)) {
setDescriptionTextStyle(typedArray.getString(R.styleable.InputRow_descriptionTextStyle));
}
but when I want to call descriptionTextView.setTypeface(Typeface tf, int style) I need an integer value for the second parameter, which I don't have.
I can't change the style to be an int format, because then ="bold" would be invalid, so I'm at a loss for how to get the text style.

Thanks to the suggestion from pskink, I took a look at how Android defines the style:
<!-- Default text typeface style. -->
<attr name="textStyle">
<flag name="normal" value="0" />
<flag name="bold" value="1" />
<flag name="italic" value="2" />
</attr>
Given that, I was able to update my custom attribute accordingly:
<attr name="descriptionTextStyle">
<flag name="normal" value="0" />
<flag name="bold" value="1" />
<flag name="italic" value="2" />
</attr>
Then I was able to setup my item by calling app:descriptionTextStyle="bold" and in the class I call typedArray.getInt() and call the setTypeface() method as I said. Thanks for the help!

You can use the
public void setTypeface(Typeface tf);
method, it only requires a typeface parameter.
Also for converting string to typeface you can do something like this:
switch (typefaceString) {
case SANS:
tf = Typeface.SANS_SERIF;
break;
case SERIF:
tf = Typeface.SERIF;
break;
case MONOSPACE:
tf = Typeface.MONOSPACE;
break;
}

Related

Understanding the xmlns attribute in an Android layout

I have only recently had to set the xmlns attribute in an Android layout file. Initially when I was adding a third-party control, certain attributes in the control's XML didn't have a prefix to identify the namespace. When I ran my app, the control was displayed but those attributes that didn't have the namespace prefix were ignored. Only after adding the xmlns to the top of my file and adding the prefix to the attributes did those attributes get recognized at run time. Here is what the corrected code looks like:
xmlns:fab="http://schemas.android.com/apk/res-auto"
<com.getbase.floatingactionbutton.FloatingActionButton
android:id="#+id/ivFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
fab:fab_icon="#drawable/ic_fab_star"
fab:fab_colorNormal="#color/pink_500"
fab:fab_colorPressed="#color/pink_100"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
android:visibility="visible"
/>
The xmlns prefix her is 'fab'. What I don't understand is that without the namespace and prefix, the app compiles without any errors. Why doesn't Android Studio complain that it cannot find fab_icon? Why does it just ignore these attributes? I have seen a number of posts throughout stackoverflow on different topics where someone has indicated to leave out the prefix and then the code worked. So I'm at a loss to understand what's going on. In some problems (like mine) having the prefix is required but in others it isn't? Is this an issue with different versions of Android Studio or the SDK versions?
Yes. Even you can define your own custom layout attributes.
Step 1: Create a subclass of a view.
class PieChart extends View {
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
Step 2: Define Custom Attributes with <declare-styleable> in res/values/attrs.xml .
<resources>
<declare-styleable name="PieChart">
<attr name="showText" format="boolean" />
<attr name="labelPosition" format="enum">
<enum name="left" value="0"/>
<enum name="right" value="1"/>
</attr>
</declare-styleable>
</resources>
Step 3: Using those attributes inside your layout xml.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.example.customviews">
<com.example.customviews.charting.PieChart
custom:showText="true"
custom:labelPosition="left" />
</LinearLayout>
Step 4: Applying custom attributes to your view.
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0);
try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}
Step 5: Adding properties and events
Attributes are a powerful way of controlling the behavior and appearance of views, but they can only be read when the view is initialized. To provide dynamic behavior, expose a property getter and setter pair for each custom attribute. The following snippet shows how PieChart exposes a property called showText
public boolean isShowText() {
return mShowText;
}
public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}
For more information and details, read this link.

Manifest fallbacks for screen orientation

In the documentation for the Android manifest, there are multiple different ways to specify screenOrientation:
landscape
sensorLandscape added in API 9
userLandscape added in API 18
How can I specify userLandscape, but on older versions of Android, have it fallback to sensorLandscape, and on even older versions fall back to landscape? I couldn't find how to do this in the documentation.
I don't think that there's a way to implement the fallback mechanism in the manifest itself.
I would suggest that you specify one of { userLandscape, sensorLandscape, landscape } in the manifest. Then, check for the version at runtime and improvise.
Say, you decide to go with android:screenOrientation="userLandscape" in the manifest.
In your activity's onCreate(Bundle), before setting the content:
int sdkInt = Build.VERSION.SDK_INT;
// if we're running on some API level within [9, 18), use `sensorLandscape`
if (sdkInt >= Build.VERSION_CODES.GINGERBREAD /* 9 */
&& sdkInt < Build.VERSION_CODES.JELLY_BEAN_MR2 /* 18 */) {
setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} else if (sdkInt < Build.VERSION_CODES.GINGERBREAD /* 9 */) {
setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
// API 18 or above - handled in manifest
setContentView(R.layout.whatever);
Hope that someone comes up with a better solution than this. This seems brute-force.
Edit:
Tried a different approach - from what I know (and I could be wrong here), enums such as userLandscape, sensorLandscape etc. won't change values. As they currently stand:
<attr name="screenOrientation">
<enum name="unspecified" value="-1" />
<enum name="landscape" value="0" />
<enum name="portrait" value="1" />
<enum name="user" value="2" />
<enum name="behind" value="3" />
<enum name="sensor" value="4" />
<enum name="nosensor" value="5" />
<enum name="sensorLandscape" value="6" />
<enum name="sensorPortrait" value="7" />
<enum name="reverseLandscape" value="8" />
<enum name="reversePortrait" value="9" />
<enum name="fullSensor" value="10" />
<enum name="userLandscape" value="11" />
<enum name="userPortrait" value="12" />
<enum name="fullUser" value="13" />
<enum name="locked" value="14" />
</attr>
So, if you were to define an integer such as:
<!-- `0` for `landscape` -- defined in values/integers.xml -->
<integer name="customScreenOrientation">0</integer>
<!-- `6` for `sensorLandscape` -- defined in values-v9/integers.xml -->
<integer name="customScreenOrientation">6</integer>
<!-- `11` for `userLandscape` -- defined in values-v18/integers.xml -->
<integer name="customScreenOrientation">11</integer>
You could then use #integer/customScreenOrientation as the value for android:screenOrientation in your activity's tag.
Needless to say its a hack at best. If someone could confirm the steady status of enum values for screenOrientation, this could be a viable workaround - preferable to including code from my earlier suggestion in multiple activities.
Yet another edit:
The second approach I mentioned earlier can be improved upon:
Instead of multiple integers.xml files, create 3 styles.xml files. I guess you already have one - values/styles.xml. Create values-v9/styles.xml & values-v18/styles.xml.
<!-- values/styles.xml -->
<style name="AppTheme" parent="#style/BaseTheme">
<item name="android:screenOrientation">landscape</item>
</style>
<!-- values-v9/styles.xml -->
<style name="AppTheme" parent="#style/BaseTheme">
<item name="android:screenOrientation">sensorLandscape</item>
</style>
<!-- values-v18/styles.xml -->
<style name="AppTheme" parent="#style/BaseTheme">
<item name="android:screenOrientation">userLandscape</item>
</style>
Following this, create values/integers.xml(one file) and define an integer customScreenOrientation:
<integer name="customScreenOrientation">?android:attr/screenOrientation</integer>
You activity tag will look like:
<activity
....
android:theme="#style/AppTheme"
android:screenOrientation="#integer/customScreenOrientation"/>
The advantage of this approach over the second one is that we get to use the enums in place of hard-coded values. Again, these two approaches are equivalent if enum-values are set in stone. If they do change, the second approach will fail while the third one keeps on going.

Create Custom Compound View in Android with Attributes

I have created a custom compound view inside a library application and everything was OK. When I add custom attributes to view, I always get default values. I followed this steps with only one difference: my view is in a library project.
/res/values/attrs.xml
<resources>
<declare-styleable name="DatePickerView">
<attr name="showToday" format="boolean" />
<attr name="calendar" format="enum">
<enum name="jalali" value="0" />
<enum name="gregorian" value="1" />
</attr>
</declare-styleable>
</resources>
Layout file that contains view:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:farayan="http://schemas.android.com/apk/lib/net.farayan.android.view"
...
<net.farayan.android.view.datepicker.DatePickerView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
farayan:showToday="false"
farayan:calendar="gregorian"/>
...
Component's code:
int calendar;
boolean showToday;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePickerView, 0, 0);
try {
calendar = a.getInteger(R.styleable.DatePickerView_calendar, 0);
showToday = a.getBoolean(R.styleable.DatePickerView_showToday, true);
} finally {
a.recycle();
}
calendar and showToday are always 0 and true respectively. Any idea?
It looks like something is not right here:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:farayan="http://schemas.android.com/apk/lib/net.farayan.android.view"
What about change to:
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:farayan="http://schemas.android.com/apk/res/net.farayan.android.view"
net.farayan.android.view is your app root namespace.
update1
xmlns:farayan="http://schemas.android.com/apk/res/your_main_app_package"
Here is am example. It use a view defined in the library project.
If we add new compound view code and its attributesinside project, we should add this at the beginning of layout:
xmlns:custom="http://schemas.android.com/apk/res/your_main_app_package
and if new compound view is inside a library project linked to our peoject, we should add this:
xmlns:custom="http://schemas.android.com/apk/res-auto
Link: https://stackoverflow.com/a/10217752/1152549

Styleable cannot be resolved

Here is the code I am using:
public ASSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray sharedTypedArray = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ASSwitch,
0, 0);
try {
onText = sharedTypedArray.getText(R.styleable.ASSwtich_onText, null);
} finally {
sharedTypedArray.recycle();
}
}
Here is the attrs.xml file (added to values folder):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ASSwitch">
<attr name="onText" format="string" />
<attr name="offText" format="string" />
<attr name="onState" format="boolean" />
<attr name="toogleDrawable" format="string" />
<attr name="frameDrawable" format="string" />
</declare-styleable>
</resources>
The answers in these questions couldn't fix the problem. Please don't consider my question as duplicate.
Android Hello, Gallery tutorial -- "R.styleable cannot be resolved"
Android: How to Declare Styleable in R.java?
R.styleable can not be resolved, why?
R.styleable cannot be resolved
Update: It seems that I was importing the wrong R class. It shall be the application's R class not android.R.
Check your imports:
Wrong: Android.R
Correct: com.example.yourproject.R
I had the same error when made this customized view. Maybe when follow the guiding step, the helper tool automatically inserts this wrong import.
It seems that I was importing the wrong R class. It shall be the application's R class not android.R

How can I use a resource within a custom Xml resource file?

I have an XML resource file:
<resources>
<section>
<category value="1" resourceId="#xml/categoryData1" />
<category value="2" resourceId="#xml/categoryData2" />
<category value="3" resourceId="#xml/categoryData3" />
</section>
</resources>
Using XmlPullParser, on the START_TAG, I can use:
int value = parser.getAttributeIntValue(null, "value", 0);
to get values 1, 2, 3...however:
int resourceId = parser.getAttributeIntValue(null, "resourceId", 0);
doesn't work...it just yields the default value 0, or whatever I change the default value (3rd parameter) to be...
Does anyone know what I am doing wrong or if this is possible?
well, if you look in your resourceId attribute, it does not seem to contain an int, does it? Or am I misreading you?
you should instead use getAttributeValue and cast it in the right Type.

Categories

Resources