I have an Android app which can create multiple lines each of which contains two spinners and an EditText. If the content gets too wide for the EditText, it breaks into multiple lines and this messes up my layout. So I call setMaxLines(1). But now the EditText scrolls internally. I want the EditText to expand horizontally to fit its content and the containing HorizontalScrollView to scroll. How can I do this?
I tried putting a HorizontalScrollView in a ScrollView and vice versa. Neither way round works. I expected that setting (both) LayoutParams to wrap_content would work but it doesn't.
I haven't included all of the code because it is a big app. Here is the layout XML:-
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- This is an invisible view to request the focus when an EditText gets deleted
in order to stop Android highlighting something else.
It would be nicer to go back into touch mode,
but Android does not provide a way of doing that. -->
<TextView
android:layout_width="1dp"
android:layout_height="1dp"
android:text=""
android:alpha="0"
android:id="#+id/defineinvisible"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/defineclasslayout">
</LinearLayout>
</LinearLayout>
</ScrollView>
</HorizontalScrollView>
The lines containing the EditTexts are added dynamically because the number of them can vary as the app runs.
The code to add a line looks a bit like this (I've left out some irrelevant detail):-
public class OrItem extends LinearLayout implements TextWatcher {
public OrItem(Context context) {
m_nameSelector = new Spinner(m_context);
m_contSelector = new Spinner(m_context);
m_matchString = new EditText(m_context);
m_matchString.setHorizontallyScrolling(false); m_matchString.setMaxLines(1);
m_matchString.setInputType(TYPE_CLASS_TEXT);
}
public setup(...) {
ViewGroup.LayoutParams ww = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
setOrientation(LinearLayout.HORIZONTAL);
addView(m_nameSelector, ww);
addView(m_contSelector, ww);
m_matchString.setText(sa[2]);
m_wasEmpty = sa[2].isEmpty();
m_matchString.addTextChangedListener(this);
addView(m_matchString, ww);
}
}
The OrItem is added under a hierarchy of LinearLayouts that hangs from defineclasslayout.
I tried inserting
but this goes back to line wrapping inside the EditText.
I found workaround for this. I make each individual line a HorizontalScrollView. Then I can scroll a long line. This is less elegant than scrolling the whole view, but it works.
public class OrItem extends HorizontalScrollView implements TextWatcher {
public OrItem(Context context) {
m_layout = new LinearLayout(context);
m_nameSelector = new Spinner(context);
m_contSelector = new Spinner(context);
m_matchString = new EditText(context);
m_matchString.setMaxLines(1);
m_matchString.setInputType(TYPE_CLASS_TEXT);
m_matchString.setHorizontallyScrolling(false);
}
public boolean setup(...) {
ViewGroup.LayoutParams ww = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
addView(m_layout);
m_layout.setOrientation(LinearLayout.HORIZONTAL);
m_layout.addView(m_nameSelector, ww);
m_layout.addView(m_contSelector, ww);
m_matchString.addTextChangedListener(this);
m_layout.addView(m_matchString, ww);
}
Related
I would like to know how to add new strings at the bottom of a ScrollView every time I press a button.
For example at the beginning there is sentence1, press button, then sentence2 is under sentence1, press button, sentence3 is under sentence2, etc
I know how to make a scrollView and I have an array of strings to display:
final int[] sentences = new int[]{
R.String.sentence1,
R.String.sentence1,
R.String.sentence2,
R.String.sentence3,
R.String.sentence4
};
And I know how to make them appear one after another when a button is pressed (kind off replacing the previous one, like a TextSwitch but without the animation) :
if(nextSentenceId < sentences.length) {
officeOBSDialog.setText(sentences[nextSentenceId]);
++nextSentenceId;
}
Do you have any idea how I could manage to do that or what could I use? It occured to me that I could use like a layout inflator but I don't know how to put that to practice and where to put it. Thanks in advance
I recommend you to use ListView or RecyclerView.
https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView
However, if you consistently want to use ScrollView cause your screen UI is simple. You can simply wrap a LinearLayout with vertical orientation by a ScrollView.
activity.xml
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<LinearLayout
android:id="#+id/lnContainer"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- your button declaration -->
</LinearLayout>
</ScrollView>
In your activity java file, add new row programmatically by:
private int position=0;
final int[] sentences = new int[]{
R.String.sentence1,
R.String.sentence1,
R.String.sentence2,
R.String.sentence3,
R.String.sentence4
};
//inside onCreate() method
yourButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View view){
TextView textView = new TextView(YourActivityClass.this);
textView.setText(sentences[position++]);
((LinearLayout)findViewById(R.id.lnContainer)).addView(textView);
}
});
I'm trying to add a bunch of ImageView on my UI using a loop, the problem is that I have no Idea if the ImageView is being added or not because when I run the app it just gives me a blank white screen.
for(int i = 0; i < jsonArray.length(); i++) {
Log.d("test", "ok"); //the loop works btw
poster.setId(i);
JSONObject jsonObject = jsonArray.getJSONObject(i);
ImageView poster = new ImageView(getApplicationContext());
poster.setBackgroundResource(R.drawable.myPoster);
RelativeLayout.LayoutParams posterParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT
);
posterParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
posterParams.addRule(RelativeLayout.CENTER_VERTICAL);
posterParams.width = 160; //is this DP?
posterParams.height = 220;
relativeLayout.addView(poster, posterParams);
}
Any suggestion is welcome.
EDIT
I added another piece of code just to test if a widget will be added without using a loop:
//test
LinearLayout layout = new LinearLayout(this);
Button btn = new Button(this);
btn.setText("this is a button");
btn.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
layout.addView(btn);
And I still get the same result, just blank.
First of all, you are giving exactly the same parameter to each ImageView this causes all your ImageView lays on Like STACK only the last one will be visible to you. You may use ScrollView to see if the ImageViews actually added to your root layout
Secondly, set layout parameters to your dynamic ImageViews not your root layout.
Update
How to use ScrollView,
First of all, your XML should contain ScollView and a child (LinearLayout in our case but you can choose any layout depending on your use case) for placing your ImageView
<ScrollView>
<LinearLayout
android:id = "imageViewPlaceHolder">
</LinearLayout>
</ScrollView>
Secondly, in your Java code, you should find an inner layout to add views as follows
LinearLayout placeHolder = findViewById(R.id.imageViewPlaceHolder);
Then you are ready to add ImageViews into placeHolder since your placeHolder wrapped with ScrollView it will create a scroll dynamically if the content height goes beyond the dedicated height.
placeHolder.addView(yourImageView);
Adding everything from Java
HorizontalScrollView hsv = new HorizontalScrollView(context);
hsv.setLayoutParams(yourLayoutParams);
LinearLayout myPlaceHolder = new LinearLayout(context);
myPlaceHolder.setLayoutParams(yourLayoutParamsForLinearLayout);
myPlaceHolder.addView(yourDesiredImageView);
hsv.addView(myPlaceHolder);
yourRootLayout.addView(hsv);
Hope it makes sense, feel free to ask any clarification
try this:
listView = (ListView) findViewById(R.id.lv_info);
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, text);
listView.addHeaderView(creatHeader(getString(R.string.abuot_email),getResources().getString(R.string.email_info)));
listView.addHeaderView(creatHeader(getString(R.string.about_phone),getResources().getString(R.string.admin_phone_num)));
listView.addHeaderView(creatHeader(getString(R.string.about_copyright), getResources().getString(R.string.copyright_info)));
listView.addHeaderView(creatHeader(getString(R.string.about_version),getResources().getString(R.string.version_info)));
listView.setAdapter(adapter);
Method creatHeader:
public View creatHeader(String title, String text) {
View v = getLayoutInflater().inflate(R.layout.lv_item_header_info, null);
((TextView) v.findViewById(R.id.tvTitle)).setText(title);
((TextView) v.findViewById(R.id.tvText)).setText(text);
return v;
}
Layout file for items:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/activity_vertical_margin">
<TextView
android:id="#+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ssss"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"/>
<TextView
android:id="#+id/tvText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="sssssssss"
android:gravity="center_vertical"/>
</LinearLayout>
This is suppose to be a scroll view with all the content added from the Java code when response is received from the API.
The problem is that I can't find a way to display the information like this in a ScrollView. I tried using an ImageButton but I couldn't get the content in it then I tried using a Button but still couldn't achieve the desired effect please can someone suggest a way I could do this.
private Button makeButton(String targetName, final String i, LinearLayout.LayoutParams buttonLayoutParams) {
Button in = new Button(this);
in.setBackground(getResources().getDrawable(R.drawable.rectangle14));
in.setText(targetName);
in.setWidth(360);
in.setHeight(72);
in.setLayoutParams(buttonLayoutParams);
in.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent myIntent = new Intent(HomeActivity.this,XSavingDetailsActivity.class);
myIntent.putExtra("i" ,i);
HomeActivity.this.startActivity(myIntent);
}
});
return in;
}
You should use a RecyclerView .Each and every component within the RecyclerView is a CardView . Also you should learn about Material Design.
Apart from the above some useful links:
https://developer.android.com/guide/topics/ui/layout/cardview.html
https://www.androidhive.info/2016/01/android-working-with-recycler-view/
https://medium.com/#nileshsingh/android-cardview-101-everything-you-should-know-5bbf1c873f5a
Just make the top-level layout a ScrollView:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TableLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:stretchColumns="1">
<!-- everything else -->
</TableLayout>
I've created a Vertical LinearLayout with a Horizontal LinearLayout within it in order to hold 3 EditText boxes within them for data entry. I've also placed a button below the vertical LinearLayout that I want to program to add another row of editText with every press.
I've succeeded so far and managed to programmatically add another row with 1 edittext in it. Then I tried with 3 and hit a roadblock. I tried to use weightsum and weights to get the editText boxes to be in 3 columns. However with the code below I get 2 editText boxes next to each other then a very very tiny editbox as the last one.
What am I doing wrong? Why aren't the widths of the edittext boxes being divided by 3?
XML:
<LinearLayout android:id="#+id/layout_ingredients"
android:layout_below="#id/text_recipeIngredients"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:weightSum="3"
android:orientation="horizontal">
<EditText android:id="#+id/edit_recipeIngredient"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:hint="#string/edit_recipe_ingredient"/>
<EditText android:id="#+id/edit_recipeAmount"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:hint="#string/edit_recipe_amount"/>
<EditText android:id="#+id/edit_recipeUnit"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:hint="#string/edit_recipe_unit"/>
</LinearLayout>
</LinearLayout>
<Button android:id="#+id/btn_add_ingredient"
android:background="#color/orange"
android:layout_marginTop="10dp"
android:layout_below="#id/layout_ingredients"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="#string/btn_add_ingredient"
android:onClick="addIngredients"/>
Java:
public void addIngredients(View view) {
final String ADDINGREDIENT = "ADD INGREDIENT";
Log.d(ADDINGREDIENT, ADDINGREDIENT);
LinearLayout linearLayout = (LinearLayout)findViewById(R.id.layout_ingredients);
LinearLayout ingredientLayout = new LinearLayout(this);
ingredientLayout.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
ingredientLayout.setOrientation(LinearLayout.HORIZONTAL);
ingredientLayout.setWeightSum(3f);
EditText editTextIngredient = new EditText(this);
editTextIngredient.setHint("Ingredient");
editTextIngredient.setLayoutParams(new LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
1f));
EditText editTextAmount = new EditText(this);
editTextAmount.setHint("Amount");
editTextAmount.setLayoutParams(new LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
1f));
EditText editTextUnit = new EditText(this);
editTextAmount.setHint("Unit");
editTextAmount.setLayoutParams(new LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
1f));
ingredientLayout.addView(editTextIngredient);
ingredientLayout.addView(editTextAmount);
ingredientLayout.addView(editTextUnit);
linearLayout.addView(ingredientLayout);
}
The problem here is that you are adding the weighted layouts to a layout that is not on the screen, so its width is unknown at the time you add them.
You have a couple of approaches, but the easiest one is probably to grab the widths of the elements already on the screen and simply duplicate them (since you are using a weighted layout to begin with).
You could also measure the screen and use that measurement to set the widths of each layout.
Last, you could use ViewTreeObserver to add them after the parent has been added and the width is known (see the post here: Getting the width/height of a layout in Android).
The last is the most "interesting" but the least efficient and useful since your original layout already did the calculations.
I have layout with controls:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contact_phones_layout_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/contact_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:inputType="phone" />
<Spinner
android:id="#+id/contact_phone_type"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
And I want to inflate it into another layout on the fragment. Quantity of these controls depends on values in a array. Array contains controls objects. So every time onCreateView rises I filled the layout from the array:
private void addLine(PhoneLine line) {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = line.getParent();
((ViewGroup) view.getParent()).removeView(view);
layout.addView(view, layout.getChildCount() - 1);
setButtonVisible(false);
}
if line is null controls are created this way:
private void addLine() {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = inflater.inflate(R.layout.contact_phone_line, layout, false);
EditText phoneEditText = (EditText) view.findViewById(R.id.contact_phone);
Spinner phoneType = (Spinner) view.findViewById(R.id.contact_phone_type);
phoneLines.add(new PhoneLine(phoneEditText, phoneType, view));
layout.addView(view, layout.getChildCount() - 1);
}
But after that I get same values in all EditText/Spinner controls, values equal to last element in the array. What can be wrong? May be there is more pure way to add controls dynamically?
I fixed it by adding controls when onResume rises, not onCreateView. But I still don't understand why onCreateView changes all values.