Background
I have an App with multiple custom TextViews in it.
These four Custom TextViews are all set up like this:
public class TextView_Light extends android.support.v7.widget.AppCompatTextView {
public TextView_Light(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public TextView_Light(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TextView_Light(Context context) {
super(context);
init();
}
public void init() {
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "fonts/Roboto-Light.ttf");
setTypeface(tf ,1);
}}
I do have TextView_Bold, TextView_Light, TextView_Regular and TextView_Thin. Each of them uses another font which is saved in assets/fonts
I use the TextViews like that:
<de.mayr.wap.app.helper.TextView_Light
android:id="#+id/textView_Light7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="#string/overviewViaBarcode"
android:textSize="16sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
When the App is started and a view shows up for the first time everything looks great, like that:
The Problem
In this view the user klicks on one of the tablerows, an Dialog appears where the user types in some data. On save the onResume method of the view is called, which calls an Webservice etc. like that:
#Override
public void onResume() {
super.onResume();
callSerNrWS();
}
public void callSerNrWS() {
if (CheckNetwork.isNetworkAvailable(getContext())) {
final AsyncResponseFortschritt<ResponseObject<PruefauftraegeVO>> response = new AsyncResponseFortschritt<ResponseObject<PruefauftraegeVO>>() {
#Override
public void processFinishSerNr(ResponseObject<PruefauftraegeVO> output) {
anw = createPruefAnwArraylist();
seriennummerVO = findDataToSerNr();
adapt = new PruefAnwAdapter(getContext(), anw, seriennummerVO);
pruefListView.setAdapter(adapt);
setVisibilityLoadAnimation(View.GONE);
if (output.getException() != null && !output.getException().isEmpty())
AlertCreator.makeFailAlert(R.string.titelKeinFortschritt, R.string.textKeinFortschritt,getContext());
}
#Override
public void processStartSerNr() {
setVisibilityLoadAnimation(View.VISIBLE);
}
};
AsyncCallSerNrWS ws = new AsyncCallSerNrWS(response, pruefauftraegeVO);
ws.execute();
}
}
And after that, theres in 90% of cases one or more TextView which changed its looking from eg. TextView_Light to TextView_Thin. Like in the following picture: The appearence of the 'Anzugstrom [A]' in the third row now looks like a TextView_Thin and the '9/48' in the fifth row looks like a TextView_Light
What I know
This doesnt happen when the view is shown for the first time
There isn't any system behind it. It's totally random.
It happens in activitys and fragments
It happens in every view i have whatever the TextView contains 'hard-coded' text or text that comes from an Webservice
Its not just in lists, also in 'normal' views
Related
I'm using Android SharedPreferences API to build a settings screen.
I've one setting which I need a long text to explain the user its meaning (I would like to have something like main title and smaller subtitle,but i think it would require to much customization)
the settings.xml is like:
<EditTextPreference
android:defaultValue="15"
android:inputType="number"
android:icon="#drawable/ic_timer"
android:key="#string/pref_comment_interval"
android:persistent="true"
android:lines="2"
android:title="#string/time_between_comments" />
but even setting lines=2 and breaking the line with \n at time_between_comments the text is getting wrapped.
<string name="time_between_comments">Time between comments (in seconds)\nLower is faster</string>
like:
how can i make the text to break the line?
By default, the title of EditTextPreference is singleLine="true"
So we should custom it as below
public class MultiLineTitleEditTextPreference extends EditTextPreference {
public MultiLineTitleEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MultiLineTitleEditTextPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MultiLineTitleEditTextPreference(Context context) {
super(context);
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
TextView textView = (TextView) view.findViewById(your_package.R.id.title);
if (textView != null) {
textView.setSingleLine(false);
}
}
}
This doesn't work for all Preference types, but it works for EditTextPreference.
Add <![CDATA[ \n]]> around the first line of your title like in this example:
<string name="pref_title_special_with_note"><![CDATA[Special Title\n]]><small><i> ** followed by second line note</i></small></string>
This will display the second line in smaller text size and italic, but that's just decoration.
Is there a way to change how the detection of autoLink TextView finds phone numbers?
Thing is, it detects international format quite well, like +49123456789 but it fails on local formatted numbers like 0699777666555 (without a preceeding "+" character).
We need to have those numbers available too.
The TextView is set up with autoLink="all"
<TextView
android:id="#+id/chat_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
... some other settings ...
android:autoLink="all"
android:linksClickable="true"
android:textColorLink="#color/darkblue"
android:textColor="#color/black"/>
We have internal numbers (like 5532) and local phone numbers without any prefixes like 12345678. It would be great, if they can be highlighted too, without any, or at least without too much coding involved.
Any solutions to this?
Thanks in advance!
Try to do this programatically:
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);
}
}
and then use AutoLinkifyTextView instead of TextView
When a user want to paste text in MultiAutoCompleteTextView I want to overwrite it.
Means whatever he copies it must paste in my MultiAutoCompleteTextView as "Java is rocket".
I may achieve it from previous question here but I don't know how to hook the class MonitoringEditText to my MultiAutoCompleteTextView.
Can I achieve it or it is impossible.
You can do this using the same concept provided in the code from the link you provided. Extend the MultiAutoCompleteTextView and override the onTextContextMenuItem method.
Something along the lines of:
public class MonitoringMultiAutoCompleteTextView extends MultiAutoCompleteTextView {
public MonitoringMultiAutoCompleteTextView(Context context) {
super(context);
}
public MonitoringMultiAutoCompleteTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MonitoringMultiAutoCompleteTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//based on http://stackoverflow.com/a/14981376/1566836
#Override
public boolean onTextContextMenuItem(int id) {
// Do your thing:
boolean consumed = super.onTextContextMenuItem(id);
// React:
if (android.R.id.paste == id) {
setText("Java is rocket");
}
return consumed;
}
}
Then change your MultiAutoCompleteTextView in your layout file to whatever.your.full.package.is.MonitoringMultiAutoCompleteTextView.
After that, any attempt to paste into your MultiAutoCompleteTextView will result in the text being changed to "Java is rocket"
I'm developing a View that shows a stack of messages making fadeIn,fadeOut and wait time between them. It has a stack of messages and when I need to display some of them I added to it. This view hasn't a specific design so the user can customize like he wants.
I have written well the 3 super constructors.
The XML layout is the next:
[... Others views ...]
<com.example.lectorrss.Views.StatusMessage
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/statusMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_alignParentBottom="true"
android:background="#6894ed">
<ProgressBar
android:id="#+id/progressBar"
android:layout_centerVertical="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/message"
android:layout_toRightOf="#id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="Hola"
android:textColor="#fff"></TextView>
</RelativeLayout>
</com.example.lectorrss.Views.StatusMessage>
[... Other views ...]
In this case, I want my custom view shows the views that are inside (progress bar + textview) and then apply the behaviour I want.
The code of my CustomView is:
public class StatusMessage extends ViewGroup{
private List<String> messagesStack;
private Worker worker;
public StatusMessage(Context context) {
super(context);
}
public StatusMessage(Context context, AttributeSet attrs){
super(context, attrs);
}
public StatusMessage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
TextView txtView = (TextView) this.findViewById(R.id.message);
if(txtView == null){
Log.d("BrowsePortal", "TextView null");
}else{
// HERE, THE CODE CAN FIND OK THE VIEW I WANT
Log.d("BrowsePortal", "TextView not null");
}
worker = new Worker(this);
worker.execute();
}
[Other logic code about view]
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
}
}
But as my view needs time of wait between messages, and uses animation, I need to create an AsyncTask:
public class Worker extends AsyncTask<Void,String,Void>{
private StatusMessage view;
public Worker(StatusMessage view) {
this.view = view;
}
#Override
protected Void doInBackground(Void... arg0) {
[I supressed all logic code here]
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
TextView txtView = (TextView) view.findViewById(R.id.message);
if(txtView == null){
// But here the code return null
Log.d("BrowsePortal", "TextView null");
}else{
Log.d("BrowsePortal", "TextView not null");
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
[I supressed all logic code here]
}
}
My problem is that from asynctask, on the progressUpdate, when I need to access to the textview, findviewbyid returns null. But trying to access the same view from custom view class instead asynctask, it works. Why from view class I can access to textview but from asynctask not?
Also, in post execute, the same code returns me null.
My asynctask receives well in her constructor the view so from here would work well as from customview.
The issue was with this line:
import android.R which I change to import [app_package].R;.
Then, when I put this line:
TextView txtView = (TextView) view.findViewById(R.id.message);
... the method could not find the textview because was using as ID, a resource from android.R instead of package.R.
The most strange is that along activities I made this time ago, sometimes I did the same mistake, but I could fix quickly because Eclipse said me that the resource specified wasnt found underlining the resource line, but for some reason, in this case didn't notify me. Maybe R.id.message is reserved for android.R.
I'm facing a problem where I know the root cause but don't see a way to fix it. If a custom compound component is used multiple times in an activity, the values saved from views will overwrite each other. To explain it easier I made the following example.
The xml for the new component, only an EditText to make it shorter.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<EditText
android:id="#+id/custom_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number" >
</EditText>
</merge>
The class implementing the new behavior, only inflating the layout.
public class CustomView extends LinearLayout {
public CustomView(Context context) {
this(context, null);
}
public CustomView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.custom_view, this, true);
}
}
And a layout using 2 of them.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<test.customview.CustomView
android:id="#+id/customView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</test.customview.CustomView>
<test.customview.CustomView
android:id="#+id/customView2"
android:layout_width="fill_parent"
android:layout_height="wrap_content" >
</test.customview.CustomView>
</LinearLayout>
When the screen is rotated, the value from second View is also restored in the first one.
Digging into the framework's code I found out that Parcelable objects returned from onSaveInstanceState defined in View class are put in a SparseArray with the key object's id. Because I'm including CustomView multiple times the EditText with id "custom_text" is also getting added multiple times. Having the same id, values saved will overwrite each other.
I'm looking for any suggestion on how this should be actually implemented. Right now, I don't see any way to change those identifiers.
Seems like I have some solution with this problem. I try to find it for some time.
1.First you must create inner class which extends BaseSavedState, inside your CustomView.
CustomView{
String value; //some text value from edittext
EditText edittext;
...
private static class Save extends BaseSavedState{
String savedValue;
public Save(Parcel incoming) {
super(incoming);
savedValue = incoming.readString();
Log.i("Save", "Parcel");
}
public Save(Parcelable parcelable) {
super(parcelable);
Log.i("Save", "Parcelable");
}
#Override
public void writeToParcel(Parcel outcoming, int flags) {
super.writeToParcel(outcoming, flags);
outcoming.writeString(savedValue );
Log.i("Save", "writeToParcel");
}
public static final Parcelable.Creator<Save> CREATOR =
new Creator<CustomView.Save>() {
#Override
public Save[] newArray(int size) {
Log.i("Parcelable.Creator<Save>", "newArray");
return new Save[size];
}
#Override
public Save createFromParcel(Parcel incoming) {
Log.i("Parcelable.Creator<Save>", "createFromParcel");
return new Save(incoming);
}
};
}
}
2.then override this two methods in CustomView
CustomView{
String value; //some text value from edittext
EditText edittext;
...
#Override
protected Parcelable onSaveInstanceState() {
Log.i("CustomView", "onSaveInstanceState");
Parcelable p = super.onSaveInstanceState();
Save save = new Save(p);
save.savedValue = value; // value is from CustomView class
return save;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
Log.i("CustomView", "onRestoreInstanceState");
if(!(state instanceof Save)){
super.onRestoreInstanceState(state);
return;
}
Save save = (Save) state;
value = save.savedValue;
//setting in this place value to edittext will not do anything.
//instead, you have to do this in step 3
super.onRestoreInstanceState(save.getSuperState());
}
...
}
3.override onAttachedToWindow() and set to edittext "value".
CustomView{
String value; //some text value from edittext
EditText edittext;
...
#Override
protected void onAttachedToWindow() {
edittext.setText(value);
super.onAttachedToWindow();
}
...
}
and now you can have multiple instances of your Custom View 's that are resistant to change orientation - they will have the correct values.I have not tested this solution in 100% but it seems to be good.