I know this has already been asked, but I tried everything and I couldn't solve my problem.
When I create the views programmatically, they are definitely added. I checked in the debugger and everything is in it's place, even the parent view gets bigger in height because they are using space. But I can't see them. It's like they are below other views or invisible (but they are not. I checked many times...).
This is the xml code where I'm trying to insert the views. I want to insert them where the cursor is (where it's tagged information). I only have it there to show you how it will look like in the end, but this part will be added programmatically.
<LinearLayout
android:id="#+id/llhTestItem"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:orientation="horizontal">
<TextView
android:id="#+id/tvInformationTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="17sp"
fontPath="fonts/OpenSans-Regular.ttf"
android:text="Sub title: "/> <!-- tvInformationTitle -->
<TextView
android:id="#+id/tvInformation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
fontPath="fonts/OpenSans-Light.ttf"
android:text="information"/> <!-- tvInformation -->
</LinearLayout> <!-- information -->
Below you can see the code that I'm using to add the views just like in the xml above.
#Override
public void onBindViewHolder(SetupViewerHolder holder, int position) {
CardViewItem cardViewItem = cardViewItemList.get(position);
holder.tvTitle.setText(cardViewItem.getCardTitle());
for (int i = 0; i < cardViewItem.getInformationList().size(); i++){
//region Create llhItem
LinearLayout.LayoutParams llhItemParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
llhItemParams.topMargin = dipToPixels(6);
LinearLayout llhItem = new LinearLayout(context);
llhItem.setLayoutParams(llhItemParams);
llhItem.setOrientation(LinearLayout.HORIZONTAL);
//endregion
LinearLayout.LayoutParams tvInformationsParams = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
//region Create tvInformationTitle
TextView tvInformationTitle = new TextView(context);
tvInformationTitle.setLayoutParams(tvInformationsParams);
tvInformationTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 17);
if (Build.VERSION.SDK_INT < 23){
tvInformationTitle.setTextAppearance(context, R.style.OpenSansRegular);
} else {
tvInformationTitle.setTextAppearance(R.style.OpenSansRegular);
}
tvInformationTitle.setText(cardViewItem.getInformationList().get(i)[0]);
//endregion
//region Create tvInformation
TextView tvInformation = new TextView(context);
tvInformation.setLayoutParams(tvInformationsParams);
tvInformation.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
if (Build.VERSION.SDK_INT < 23){
tvInformation.setTextAppearance(context, R.style.OpenSansLight);
} else {
tvInformation.setTextAppearance(R.style.OpenSansLight);
}
tvInformation.setText(cardViewItem.getInformationList().get(i)[1]);
//endregion
llhItem.addView(tvInformationTitle);
llhItem.addView(tvInformation);
holder.llvInformation.addView(llhItem);
}
Basically what I'm trying to achieve is to have a recycler view, and each item has only one title, one overflow button, but can have multiple information rows.
Here is a print of this, which I had hard coded in xml previously as a prototype.
I know of some alternative ways of doing this that might work, but for now I would like to have it like this, since everything is working like it should, the views are just "not visible".
Had to use layout.post
holder.llvInformation.post( new Runnable() {
#Override
public void run() {
holder.llvInformation.addView(llhItem);
}
});
Have you tried calling invalidate() after adding the view? Like this:
holder.llvInformation.addView(llhItem);
holder.llvInformation.invalidate();
Related
I'm developing my very first app and I have an issue with ImageViews and Horizontalscrollview: I want to collect all the goals reached by the user adding images inside an Horizontalscrollview (goal reached --> new image displayed).
how 4 completed goals are displayed
They show properly and everything seems to work but when I open another activity and then I come back, they disappear. I tried to invalidate the layout but nothing, am I missing something?
Thank you in advance!
This is my XML file
<!-- language: lang-xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout3"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Main3Activity">
<HorizontalScrollView
android:id="#+id/horizontal_scroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="300dp">
<LinearLayout
android:id="#+id/linear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
</HorizontalScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
This is my Java code
The function I use to check if a goal is completed:
<!-- language: lang-java -->
protected void checkGoal(){
int progress = calculateProgress();
if(goalPref.getInt("Goal",0)!=0){
if(progress >= goalPref.getInt("Goal", 0)) {
int id = layout.getChildCount();
addNewImage(id); // <-- here I add the image
question.setText(R.string.setgoal);
updateGoalPref(0);
if (list.size() != 0) {
list = new ArrayList<>();
}
updateProgress();
}
}
}
The function I use to add images:
<!-- language: lang-java -->
protected void addNewImage(int id){
ImageView imageView = new ImageView(this);
imageView.setId(id);
imageView.setPadding(2, 2, 2, 2);
imageView.setBackgroundResource(R.drawable.check);
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
layout.addView(imageView, id);
}
When you go to another activity and come back, you should call addNewImage in onResume to show the images again:
#Override
protected void onResume() {
super.onResume();
addNewImage(id);
}
If the number of goals in your activity is limited, why don't you create those ImageViews in XML and set their Visibility in java?
Since I'm pretty stubborn, I managed to find another way to do it and this is what I did: using the code I posted I added a counter to keep track of how many goals the user reached and onResume() I display as many images as the counter value.
In this way, the user can see immediately the effect of reaching a goal and the activity doesn't lose anything.
Then, I implemented it to show a description of the goal, changing the ImageViews in TextViews with those images as backgrounds. Instead of using a counter, I saved the strings I wanted to display in an ArrayList and used its size as a counter.
Hope it can be useful for someone!
<!-- language: lang-java -->
protected void checkGoal(){
int progress = calculateProgress();
if(goalPref.getInt("Goal",0)!=0){
if(progress >= goalPref.getInt("Goal", 0)) {
String newString = goalPref.getInt("Goal", 0) + "€, " + MainActivity.currentDate();
goalsCompleted.add(newString);
updateGoalCompletedList();
//Here I clean the layout and display all the TextViews
layout.removeAllViews();
for(int i=goalsCompleted.size()-1; i>0; i--){
addNewTextView(i);
}
question.setText(R.string.setgoal);
updateGoalPref(0);
if (list.size() != 0) {
list = new ArrayList<>();
}
updateProgress();
}
}
}
//The function used to display the TextViews
protected void addNewTextView(int id){
TextView textView = new TextView(this);
textView.setBackgroundResource(R.drawable.check1);
textView.setId(id);
textView.setPadding(25,260,0,0);
textView.setTextSize(12);
textView.setText(goalsCompleted.get(id));
layout.addView(textView);
}
//The function used to update and save the array
public void updateGoalCompletedList(){
Gson gson3 = new Gson();
jsonItems3 = gson3.toJson(goalsCompleted);
SharedPreferences.Editor listEd = goalsCompletedPref.edit();
listEd.putString("GoalsCompleted", jsonItems3).apply();
}
//The function used to resume the array, you can call it on onResume() or onCreate()
public void resumeGoalsCompleted(){
Gson gson3 = new Gson();
String savedList = goalsCompletedPref.getString("GoalsCompleted", "");
goalsCompleted = gson3.fromJson(savedList, new TypeToken<ArrayList<String>>() {
}.getType());
if(goalsCompleted==null)
goalsCompleted = new ArrayList<>();
//From the most recent to the least recent
for(int i=goalsCompleted.size()-1; i>0; i--){
addNewTextView(i);
}
}
I have two textviews in one horizontal layout, the first is normal text and the second is clickable with a different color.
XML
<!--inside rootlayout..-->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:maxLines="2"
android:text="By clicking sign up, you agree to our "
android:textColor="#color/black"
android:textSize="12sp"/>
<TextView
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="terms and conditions"
android:textColor="#color/colorPrimary"
android:textSize="12sp"
android:clickable="true"/>
</LinearLayout>
And It gives me a great look on large screens (4.7 inch and above),
but when the screen size is lower, the second textview gets weird.! I want it to automatically position itself below the first textview or to make their parent layout orientation vertical..!!
here's how it looks.!
Update #1
why the ForegroundColorSpan won't change!? it always shows blue or black no matter what color resources I set.!??
private void handleTermsConditions() {
SpannableStringBuilder stringBuilder = new SpannableStringBuilder(termsTxt.getText());
stringBuilder.setSpan(new StyleSpan(Typeface.BOLD), 38, 58, 0);
int color = ContextCompat.getColor(RegistrationActivity.this, R.color.colorPrimary);
ForegroundColorSpan fcs = new ForegroundColorSpan(color);
stringBuilder.setSpan(fcs, termsTxt.getText().length() - 20, termsTxt.getText().length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View widget) {
Toast.makeText(RegistrationActivity.this, "Click", Toast.LENGTH_SHORT)
.show();
}
};
stringBuilder.setSpan(clickableSpan, 38, 58, Spanned.SPAN_POINT_MARK);
termsTxt.setMovementMethod(LinkMovementMethod.getInstance());
termsTxt.setText(stringBuilder);
}
The same question here or from the original document
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
Get the height and width of your device and use the values to decide whether to set screen to portrait or not:
if ((height == <value>) && (width == <value>)) {
setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
*Feel free to modify as required in your activity
for your requirement you don't have to use 2 text views for this you can place a spannable string builder on just 1 text and put clickable as well as color property and you are done.
Code:
TextView textView = findViewById(R.id.tvSample);
SpannableStringBuilder stringBuilder =new SpannableStringBuilder(textView.getText());
stringBuilder.setSpan(new ForegroundColorSpan(Color.BLUE),textView.getText().length()-20,textView.getText().length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
stringBuilder.setSpan(new ClickableSpan() {
#Override
public void onClick(final View view) {
Toast.makeText(MainActivity.this,"Click",Toast.LENGTH_LONG).show();
}
},textView.getText().length()-20,textView.getText().length(),Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(stringBuilder);
Here is example of putting different spans on text view
This is how to set two spans on single text view
You can set TextView Font size or width according to screen size using value folder. Try like this.
You can use most easy way Android Spannable property for doing this. and by that way you can do this work by single textview and can manage your click events.
Here is code for doing this.
public void setHighLightedText(TextView textView, String textToHighlight) {
String tvt = textView.getText().toString();
int ofe = tvt.indexOf(textToHighlight, 0);
ClickableSpan clickableSpan = new ClickableSpan() {
#Override
public void onClick(View textView) {
// here you do all stuff
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(0xff0000ff);
ds.setUnderlineText(true);
// Here you can put your style on textview.
}
};
SpannableString wordtoSpan = new SpannableString(textView.getText());
for (int ofs = 0; ofs < tvt.length() && ofe != -1; ofs = ofe + 1) {
ofe = tvt.indexOf(textToHighlight, ofs);
if (ofe == -1)
break;
else {
wordtoSpan.setSpan(clickableSpan, ofe, ofe + textToHighlight.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(wordtoSpan, TextView.BufferType.SPANNABLE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
}
}
}
You can see onClick method in it. there you can set click or use callback if you put this code in Utility class.
Bonus
Also this is the right way to do this.
Either use Fragments
OR
Try autoSizeText
<TextView
.......
android:autoSizeTextType="uniform"
................................../>
Here is something about it on android developer site
My activity is a Video Player Activity and its working fine, On some Videos it must throw a message (handleHardwareAccelerationError) if the video is not supported or high quality.
It shows as Toast with message fine.
Now I would like to change this Toast message to show a picture or small icon instead, is this possible? Here is my code for handleHardwareAccelerationError, Thanks in advance.
private void handleHardwareAccelerationError() {
mHardwareAccelerationError = true;
if (mSwitchingView)
return;
Toast.makeText(this, R.string.hardware_acceleration_error, Toast.LENGTH_LONG).show();
}
what I have tried is this but didnt show anything.
private void handleHardwareAccelerationError() {
mHardwareAccelerationError = true;
if (mSwitchingView)
return;
final View popupView = getLayoutInflater().inflate(R.layout.myinforbar, null);
mError = (ImageView) popupView.findViewById(R.id.errornew);
mError.setVisibility(View.VISIBLE);
}
and here is myinforbar.xml
<LinearLayout android:gravity="center_vertical" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="21dp" android:layout_marginLeft="0.0dip" android:layout_marginTop="5.0dip"
android:background="#drawable/button5_background">
<ImageView
android:id="#+id/errornew"
android:src="#drawable/everyone_icon_nearby_cur"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:visibility="invisible"/>
</LinearLayout>
EDITED:
The original activity is long and it can be found here rather then pasting the whole activity you can see it here VideoPlayerActivity
( private void handleHardwareAccelerationError)
New Update...
This is without the message (onHardwareError) applied, I want to see icon inside this bottom little box that popup when video playing (infobar) instead of Toast message.
Toast is farily limited, you cannot show icons.
Edit: Gunjav Dave says it's possible, so it's approach worth a try.
Anyway this free library: SuperToast
can make what you need.
If you prefer to stick to Google's standards you can use, instead, the Snackbars, that don't allow, as far as I know, to put an icon, but can anyway be associated with an action/button, and they are standard.
I can't understand what you try to do with popupView, if you want to use a PopupWindow you have to use a completely different syntax. This Balloon Popup library of mine helps you to make a popup attached to a View, using a PopupWindow, you can also inflate into it a layout, or use it as an example on how to make a PopupWindow.
Edit:
You are inflating a layout inside a view, but this doesn't show the view. Instead, you should use a PopupWindow, and inflate inside it a layout.
This is an example taken from my BalloonPopup library:
// ....
hostedView = ((LayoutInflater) ctx.getSystemService(LAYOUT_INFLATER_SERVICE)).inflate(layoutRes, null);
textView = (TextView) hostedView.findViewById(R.id.text_view);
textView.setText(text);
// ....
popupWindow = new PopupWindow(hostedView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
In other words, you should be able to do something like
PopupWindow popupWindow = new popupWindow(mError, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
in your code to show the popup window.
These are the essential points of the code, then you can reposition the popup, and eventually keep track of it to edit and re-show it instead of always creating from scratch. Please consider, anyway, to use BalloonPopup library, that does what you need, without getting crazy with animations, display times, window's permanence and positioning. You can just inflate into it your layout, for example with an icon and a text, and make it appear exactly on the center of another View, that can be your (already present) media player.
More edit:
Another very simple solution.
To get it simple, create a RelativeLayout, that hosts all your video player, and then create a simple ImageView in the center of it. Set its visibility to GONE when hidden and to VISIBLE when the error must be shown. Subsequently, you can use a timer or a touchListener to close it making it GONE again.
Try this
private void handleHardwareAccelerationError() {
mHardwareAccelerationError = true;
if (mSwitchingView)
return;
final View popupView = getLayoutInflater().inflate(R.layout.myinforbar, null);
PopupWindow pw = new PopupWindow(popupView , 300, 400, true);
pw.showAtLocation(popupView , Gravity.CENTER, 0, 0);
mError = (ImageView) popupView.findViewById(R.id.errornew);
mError.setVisibility(View.VISIBLE);
}
XML Name or Layout Name would be
<LinearLayout xmlns:androidclass="http://schemas.android.com/apk/res/android"
android:gravity="center_vertical"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#drawable/button5_background">
<ImageView
android:id="#+id/errornew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_launcher"/>
</LinearLayout>
Put This in Class:
LayoutInflater inflater = LayoutInflater.from(this);
View layout= inflater.inflate(R.layout.myinforbar, null);
Toast toast = new Toast(getApplicationContext());
toast.setDuration(Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
toast.setView(layout);
toast.show();
I have an AlertDialog in my app that contains an EditText field. This dialog needs to be generated programmatically and should match the way the EditTextPreference dialog looks that is displayed automatically by Android when a user edits text by touching an EditTextPreference. This all works, but the size of the EditText inserted programmatically is too wide and doesn't match the one displayed by the EditTextPreference when touched. The following two images show the problem.
EditText looks like this when added to the AlertDialog using setView():
But should look like this:
Here is the XML code in the XML responsible for the EditTextPreference:
<EditTextPreference
android:title="Enter Name"
android:key="name"
android:defaultValue=""
android:summary=""
android:inputType="textCapSentences|textMultiLine"
android:singleLine="false"
android:gravity="top|left"
android:lines="2"
android:minLines="1"
android:maxLines="2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbars="vertical"
/>
And the Java code responsible for my dialog:
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mainActivity);
alertDialog.setTitle("Enter Date");
final EditText input = new EditText(mainActivity);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
input.setLayoutParams(lp);
input.setGravity(android.view.Gravity.TOP|android.view.Gravity.LEFT);
input.setInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES|InputType.TYPE_TEXT_FLAG_MULTI_LINE);
input.setLines(1);
input.setMaxLines(1);
input.setText(lastDateValue);
alertDialog.setView(input);
You have to add a container before doing that, e.g LinearLayout
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mainActivity);
alertDialog.setTitle("Enter Date");
LinearLayout container = new LinearLayout(this);
container.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(left, top, right, bottom);
final EditText input = new EditText(mainActivity);
input.setLayoutParams(lp);
input.setGravity(android.view.Gravity.TOP|android.view.Gravity.LEFT);
input.setInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES|InputType.TYPE_TEXT_FLAG_MULTI_LINE);
input.setLines(1);
input.setMaxLines(1);
input.setText(lastDateValue);
container.addView(input, lp);
alertDialog.setView(container);
I hope it helps :)
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mainActivity);
alertDialog.setTitle("Enter Date");
final EditText input = new EditText(mainActivity);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,LinearLayout.LayoutParams.WRAP_CONTENT);
lp.setMargins(left, top, right, bottom);
input.setLayoutParams(lp);
input.setGravity(android.view.Gravity.TOP|android.view.Gravity.LEFT);
input.setInputType(InputType.TYPE_TEXT_FLAG_CAP_SENTENCES|InputType.TYPE_TEXT_FLAG_MULTI_LINE);
input.setLines(1);
input.setMaxLines(1);
input.setText(lastDateValue);
alertDialog.setView(input);
Please change your code as above and provide desire all margins
You can do this without extra container layout, only add this to your alertDialog :
alertDialog.setOnShowListener(dialog -> {
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) editText.getLayoutParams();
p.setMargins(50, 20, 50, 20); // You can change the margins for your need
editText.requestLayout();
});
Create an Android XML with the needed and formatted Elements. Inflate the XML/view and add view to dialog button.
This way the UI design and the code is cleaner separated. The Android XML Editor with preview makes it easier to get the wanted optical results.
The Example uses Databinding and so the XML/View is addressed via Databinding DialogEditTextBinding.inflate([...]) and the result is accessed via databinding dialogBinding.editText.getText().toString().
Java Code in Fragment/Activity
final DialogEditTextBinding dialogBinding = DialogEditTextBinding.inflate(getLayoutInflater());
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mainActivity);
alertDialog.setTitle("Enter Date");
alertDialog.setView(dialogBinding);
alertDialog.setPositiveButton("OK",
(dialog, whichButton) -> {
String editText = dialogBinding.editText.getText().toString();
doSomething(editText);
});
Android XML for Dialog extra Elements
dialog_edit_text.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<EditText
android:id="#+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="22dp"
android:layout_marginEnd="22dp"
android:textSize="12sp"
/>
</LinearLayout>
</layout>
PS
PS: The example is combined out of my code and your code, so it may contain errors.
Building on #LeonardoSibela answer, I made this simple function in kotlin to show a dialog with an edittext and a callback.
private fun showEditDialog(title: String, preFill: String, onSubmit: (text: String) -> Unit) {
val editText = EditText(requireContext()).apply {
setText(preFill)
}
val viewContainer = LinearLayout(requireContext()).apply {
orientation = LinearLayout.VERTICAL
val lp = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
).apply {
setMargins(16.dp, 0, 16.dp, 0)
}
addView(editText, lp)
}
MaterialAlertDialogBuilder(requireContext())
.setView(viewContainer)
.setTitle(title)
.setPositiveButton("Ok") { _, _ -> onSubmit(editText.text.toString()) }
.setNegativeButton("Cancel") { _, _ -> }
.show()
}
And you could use it like this:
showEditDialog("Change name", "") { name ->
viewModel.updateName(name)
}
I have the following layout defined in useful_numbers_item_fragment.xml:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="#+id/call_linear_layout">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/useful_nums_item_name"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/useful_nums_item_value"/>
</LinearLayout>
<ImageButton
android:layout_width="0dp"
android:layout_height="wrap_content"
android:src="#drawable/call"
android:id="#+id/call_btn"
android:onClick="callNumber"/>
</LinearLayout>
I dynamically populate the two text views in a class called UNItemListFragment.java
in the onCreate method:
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
if (getArguments().containsKey(Constants.UNItem.GROUP_ID)) {
simpleCursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.useful_numbers_item_fragment, null,
new String[]{Constants.UNItem.NAME, Constants.UNItem.VALUE},
new int[]{R.id.useful_nums_item_name, R.id.useful_nums_item_value}, 0);
setListAdapter(simpleCursorAdapter);
getLoaderManager().initLoader(0, getArguments(), this);
}
}
For each number if i click on the button i want to make a phone call by
calling the callNumber method when the user clicks the button:
public void callNumber(View view) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
TextView unItemVal = (TextView) findViewById(R.id.useful_nums_item_value);
String phoneNumber = unItemVal.getText().toString();
callIntent.setData(Uri.parse("tel:" + phoneNumber));
startActivity(callIntent);
}
It is ok when I click the first button in the list, but when I click on the other buttons
it continues calling the number defined in the first row...
Any idea how to resolve this?
The problem is that this line:
TextView unItemVal = (TextView) findViewById(R.id.useful_nums_item_value);
is executed on the activity, so the findViewById will always return the first item with that id, which is likely the first item in the list.
The best way to fix this would be to override the adapter and add a tag containing the phone number to the view. A quick way to fix this would be to tag along in the view hierarchy, like so:
public void callNumber(View view) {
if( view != null ) { // view is the button tapped
View parent = view.getParent(); // this should be the LinearLayout
if( parent instanceof LinearLayout ) {
TextView unItemVal = (TextView) ((LinearLayout)parent).findViewById(R.id.useful_nums_item_value);
if( unItemVal != null ) {
Intent callIntent = new Intent(Intent.ACTION_CALL);
String phoneNumber = unItemVal.getText().toString();
callIntent.setData(Uri.parse("tel:" + phoneNumber));
startActivity(callIntent);
}
}
}
}
This would find the parent for the button that was clicked, and then find the text-view containing the number within that ViewGroup.
Using findViewById() will return the first view in the activity or fragment with the specified id. If this is a ListView, it will correspond to the first row.
There are many ways to work around this problem. The quickest one (but certainly not the prettiest one, since it depends on the layout) would be to use findViewById() relative to the LinearLayout that contains the list item. Assuming that view is the ImageButton, it would be somthing like:
((View)view.getParent()).findViewById(R.id.useful_nums_item_value)
A more elegant solution would be to set a tag in the adapter's getView(), containing the data you need (in this case, the phone number to call).