I've done professional development for over 14 years, but I am learning Android development. I came across a scenario that boggles my mind. I designed a simple layout, then decided to move some of the buttons from one LinearLayout to another LinearLayout within the same layout file.
Now, since moving the buttons, the click events are wired to the wrong buttons! It's as if the resource ids of the buttons were order-dependent.
BEFORE (works correctly):
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/previous_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/previous_button" />
<Button
android:id="#+id/true_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/true_button"/>
<Button
android:id="#+id/false_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/false_button" />
<Button
android:id="#+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/next_button"/>
</LinearLayout>
AFTER (works incorrectly):
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/true_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/true_button"/>
<Button
android:id="#+id/false_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/false_button" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/previous_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/previous_button" />
<Button
android:id="#+id/next_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/next_button"/>
</LinearLayout>
As you can see in the "AFTER" code, I want the previous/next buttons to be below the True/False buttons. And, if you look the designer, it's flawless -- perfect! But, the controller hiccups:
True button now becomes the Previous button
False button now becomes the True button
Previous button now becomes the False button
Next button works as it should.
Here is the code that is wiring up the click events (sorry if the code looks lame, I am going through an Android tutorial book, so please don't critique it, it's not mine!):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz);
mQuestionTextView = (TextView)findViewById(R.id.question_text_view);
mQuestionTextView.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
updateQuestion();
}
});
mTrueButton = (Button)findViewById(R.id.true_button);
mTrueButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
checkAnswer(true);
}
});
mFalseButton = (Button)findViewById(R.id.false_button);
mFalseButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
checkAnswer(false);
}
});
mNextButton = (Button)findViewById(R.id.next_button);
mNextButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
updateQuestion();
}
});
mPreviousButton = (Button)findViewById(R.id.previous_button);
mPreviousButton.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v) {
int length = mQuestionBank.length;
mCurrentIndex = (mCurrentIndex + (length-1)) % length;
updateQuestion();
}
});
updateQuestion();
}
So these resource ids must be order specific; I can't explain it any other way. The problem is that I don't know how to make my buttons do what they want in the layout that I want.
OK, I think I managed to fix it. In Eclipse/ADT I went to Project -> Clean... and that seemed to do the trick. And yes, it was an issue with the resource ids! I tried posting a nice screen shot of the git repository before/after so you can see what lines from the R.java file changed but I don't have enough reputation points. Sorry.
Related
I have implemented a barcode scanner by using Tutorial how to create Barcode Reader app in Android Studio. The tutorial has used zxing integration. I am able to configure the barcode app.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="2dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:text="Smart MSN #" />
<AutoCompleteTextView
android:id="#+id/smart_msn_spinner"
android:layout_width="157dp"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:inputType="number" />
<ImageButton
android:id="#+id/start_scan"
android:layout_width="66dp"
android:layout_height="match_parent"
android:background="#color/white"
app:srcCompat="#drawable/ic_baseline_qr_code_scanner_24" />
</LinearLayout>
public class NewFormFragment extends Fragment {
#BindView(R.id.start_scan)
ImageButton startScan;
#BindView(R.id.smart_msn_spinner)
AutoCompleteTextView smartMsnSpinner;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
bindListners();
}
private void bindListners() {
.
.
.
.
startScan.setOnClickListener(startScanning);
}
private final View.OnClickListener startScanning = v -> {
if(v.getId()==R.id.start_scan){
IntentIntegrator scanIntegrator = new IntentIntegrator(getActivity());
scanIntegrator.initiateScan();
}
};
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult scanningResult = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (scanningResult != null) {
String scanContent = scanningResult.getContents();
String scanFormat = scanningResult.getFormatName();
//smartMsnSpinner.setText("FORMAT: " + scanFormat);
smartMsnSpinner.setText(scanContent);
}
}
}
When I start my application and click on the scan button I am able to scan a barcode. But After scanning I am seeing nothing in my AutoCompleteTextView i.e. smartMsnSpinner.
While debugging I saw that right after scanning the breakpoint never hits the onActivityResult
I must be missing something that I don't know.
Any help would be highly appreciated.
I have a RecyclerView in MainActivity that shows a list of CardViews and that is working properly. A click on the CardView finishes the RecyclerView Activity and launches a Detail Activity that shows the clicked on CardView in a new RecyclerView list. The Detail Activity is used only to show that single CardView in a RecyclerView (I do this so I can use RecyclerView's ItemTouchHelper.SimpleCallback code on the CardView for easy left swipe for the user).
Here is the problem: I hit the left caret on the Detail Activity's Toolbar to return to the MainActivity. Then a click on the exact same CardView brings the user back to the Detail Activity. But this time only the background View (a border) is showing. The view of the CardView and its database data is completely missing.
The error appears to happen randomly. I can click to go from the MainActivity to the Detail Activity back and forth 5 times successfully and then on the sixth try, no CardView will show in the Detail Activity. Or I'll click two times successfully and then the third time, the CardView in the Detail Activity will not show. Note the left caret click in Detail Activity uses onBackPressed() so the Detail Activity finishes. So I don't think there should be any backstack issues. I also tried to adjust the xml height for the CardView to match_parent rather than wrap_content but no luck. The Detail Activity's ViewModel to Repository to Dao returns a List wrapped in LiveData. Perhaps there is an observer problem with the ViewModel, but I thought the observer gets removed/destroyed when the Detail Activity is destroyed? What am I missing here?
Adapter
...
itemHolder.cardView.setOnClickListener(view -> {
Card adapterItem= TodosAdapter.this.getItem(itemHolder.getAdapterPosition());
int adapPos = itemHolder.getAdapterPosition();
if (adapPos !=RecyclerView.NO_POSITION) {
onItemClick(adapPos, adapterItem);
}
});
MainActivity
...
public void onItemClick(int clickPos, Card cardFromClick) {
Intent intent = new Intent(MainActivity.this, DetailActivity.class);
intent.putExtra("TAG","fromMain");
intent.putExtra("itemFromMain", cardFromClick);
startActivity(intent);
finish();
DetailActivity
...
public class DetailActivity extends AppCompatActivity {
private int cardId = -1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
// Get a new or existing ViewModel from the ViewModelProvider.
detsViewModel = new ViewModelProvider(this).get(CardViewModel.class);
Toolbar toolbar = findViewById(R.id.toolbar);
// The left caret is for Up navigation to the previous activity
// for OS versions 4.0 and earlier.
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
toolbar.setNavigationIcon(R.drawable.ic_action_previous_item);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onBackPressed();
}
});
}
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
String classname = extras.getString("TAG");
// The user clicked on a Card in the MainActivity
if (classname != null && classname.equals("fromMain")) {
card = extras.getParcelable("itemFromMain");
if (card != null) {
cardId = card.getId(); // card data is stored in Room database.
}
}
}
detsViewModel.getSingleCard(cardId).observe(this, singleAdapterList -> {
adapter2.setCardList(singleAdapterList);
});
}
activity_details.xml
...
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
tools:context=".DetailsActivity" >
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" >
</include>
<RelativeLayout
android:id="#+id/todoListLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/toolbar" >
<TextView
android:id="#+id/Card"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="6dp"
android:text="Card"
android:textStyle="bold"
android:textColor="#color/text_primary"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_centerHorizontal="true" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/details_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_below="#+id/Card"
android:scrollbars="vertical" />
<TextView
android:id="#+id/skytext5"
android:text="Cards"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/details_recyclerview"
android:background="#color/colorPrimary"
android:textAppearance="?android:attr/textAppearanceMedium"
android:clickable="true"
android:focusable="true" />
</RelativeLayout>
DetailsAdapter
...
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.details_list_item, parent, false);
}
private List<Card> oneCardList
public void setCardList(List<Card> singleCardList) {
if (oneCardList != null) {
oneCardList.clear();
this.oneCardList = singleCardList;
} else {
// First initialization
this.oneCardList = singleCardList;
}
}
details_list_item.xml
...
<FrameLayout
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/detsinglecard_view"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:orientation="vertical"
android:foreground="?android:attr/selectableItemBackground"
android:background="#FFFFFF"
tools:context=".DetailActivity">
<RelativeLayout
android:id="#+id/view_background2"
android:layout_width="wrap_content"
android:layout_height="match_parent"
...
</RelativeLayout>
<RelativeLayout
android:id="#+id/view_foreground2"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="#color/colorFlLabelFinal" >
<androidx.cardview.widget.CardView
android:id="#+id/cardview_dets"
android:layout_height="match_parent"
android:layout_width="match_parent"
...
}
ViewModel
...
LiveData<List<Card>> getSingleCard(int cardId) {
return repository.getSingleCard(cardId);
}
Repository
...
public LiveData<List<Card>> getSingleCard(int cardId) {
return quickcardDao.getSingleCard(cardId);
}
Dao
...
#Query("SELECT * FROM cards WHERE cardId = :cardId LIMIT 1")
LiveData<List<Card>> getSingleCard(int cardId);
So if the data does not change then going back to the same DetailActivity will not refresh the View. The answer was to re-use the LiveData (rather than re-loading the LiveData again from the database) if the data has not changed. See the Android Developers Architecture Components guide for ViewModel, "Implement a ViewModel" section for the "loadUsers()" example that solved my problem: https://developer.android.com/topic/libraries/architecture/viewmodel.
I am basic. in android studio, I want to start a basic project.
I want to type "any word" in a plain text view.
Only whenever I click on a button ,Just we can see that word as a TOAST.
this case I need : a plan text view, a button , and a toast.
How should i do this?
Thanks for the replies.
In your xml file you need to set an id for both textviews and button
In your activity:
//get the button
button = (Button) findViewById(R.id.but);
txt1= (text1) findViewById(R.id.text1);
txt2 = (text2) findViewById(R.id.text2);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
txt2.setText(txt1);
}
Your XML code should look like so. Note the id's of the EditText and the TextView which are crucial for extracting your strings and setting them respectively.
Also note the 'onClick' function on your button which is necessary to call upon the method in your MainActivity (Java Class) with the same name.
<LinearLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/enterText"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="changeText"
android:text="Show Now"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/newText"/>
</LinearLayout>
Now for your MainActivity (Java) Class method:
public void changeText(View view){
EditText editText = findViewById(R.id.enterText);
String res = editText.getText().toString();
TextView textView = findViewById(R.id.newText);
textView.setText(res);
}
Again take note of the declaration of the EditText object using your 'id', "enterText" and "newText" respectively.
Hope this helps.
I'm painfully new to android and I've run into a wall.
I'm trying get a linear layout to function more like a button, with different actions for press and long press - The reason being so I can have 2 differently formatted text labels on each "button". Something along the lines of:
-------------------------
| 2nd | <- Label for long press (regular/smaller type)
| = | <- Label for regular press (bold/larger type)
-------------------------
The posts I've found explain how to receive a regular click on a linear layout (I use the onClick attribute in the layout XML). But I've had no luck with long press. I've tried to define a new onLongClick attribute for xml as described in Aleksander Gralak's answer here: Long press definition at XML layout, like android:onClick does . But had no such luck - it looks like it was intended for a text view, I tried changing it to linear layout but failed miserably.
Here is the object in question: Main.xml
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:clickable="true"
android:focusable="true"
android:background="#drawable/darkgrey_button"
android:onClick="equals" android:longClickable="true" android:id="equalsbutton"
android:focusableInTouchMode="false">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2nd"
android:id="#+id/textView"
android:duplicateParentState="true"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" = "
android:id="#+id/textView1"
android:duplicateParentState="true"/>
</LinearLayout>
And Main.java
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
public void equals(View view) {
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
Add an id to your layout.
<LinearLayout
android:id="#+id/my_button_layout"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
… >
In your Main.java
Get a reference to your LinearLayout and set a OnLongClickListener.
LinearLayout buttonLayout = (LinearLayout) findViewById(R.id.my_button_layout);
…
buttonLayout.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
Toast.makeText(Main.this, "Long click!", Toast.LENGTH_SHORT).show();
return true;
}
});
I'm trying to have a preloader to show while my request to the server is waiting for response
but when I try to hide the preloader with setVisibility(View.GONE); it doesn't hide (even though the other content does get visible)
when I run the function that suppose to remove the preloader and show the other content directly (not in the callback of the async request - it works)
my layout looks like this (I removed all the irrelevant stuff like the android:text properties etc.):
[notice that the loader I'm using is a custom view I made (I don't know if it has anything to do with the problem]
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/logo" />
<views.Loader
android:id="#+id/preloader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<LinearLayout
android:id="#+id/buttonsArea"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button
android:id="#+id/my_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/my_dance"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:id="#+id/another_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
notice that the loader I'm using is a custom view (I don't know if it has anything to do with the problem)
My Java code is:
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_page);
init();
setLoading(true);
loadData();
}
private void init(){
preloader = (Loader) findViewById(R.id.preloader);
buttonsArea= (LinearLayout) findViewById(R.id.buttonsArea);
}
private void loadData(){
app.getManager().app_init(new OnAppInfoResponse(){
#Override
public void onResponse(boolean success) {
//this method runs on UI Thread through Handler
setLoading(false);
}
});
}
private void setLoading(boolean loading) {
this.loading = loading;
if(loading){
buttonsArea.setVisibility(View.GONE);
preloader.setVisibility(View.VISIBLE);
} else {
buttonsArea.setVisibility(View.VISIBLE);
preloader.setVisibility(View.GONE);
}
}
Edit:
even weirder:
when I remove the buttonsArea.setVisibility(View.VISIBLE); line, the preloader does get hidden!
Thank you very much!!