I working on a chat app.
Whenever I submit or receive a text message, I append them to the chatbox.
When the list get longer, I need to scroll down to see them.
How can I make it to autoscroll to the newly append text?
<ScrollView
android:id="#+id/scrollView01"
android:layout_width="fill_parent"
android:layout_height="100px" >
<TextView
android:id="#+id/txtChat"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
/>
</ScrollView>
//
SendMsg.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
String name = txtName.getText().toString();
String message = txtMessage.getText().toString();
if (message.length() > 0) {
sendMsg(name, message);
String myMessage = message + "\n";
tvChat.append(myMessage);
}
else
Toast.makeText(getBaseContext(),
"Please enter both name and message.", Toast.LENGTH_SHORT).show();
}
});
I had the same issue, this is what I'm using:
//delay must be expressed in milliseconds. For 3 seconds, delay = 3000
private void scrollToBottom(int delay) {
// If we don't call fullScroll inside a Runnable, it doesn't scroll to
// the bottom but to the (bottom - 1)
mChatBox.postDelayed(new Runnable() {
public void run() {
mChatBox.fullScroll(ScrollView.FOCUS_DOWN);
}
}, delay);
}
where mChatBox is:
ScrollView mChatBox;
Related
I have a button(button) and a textview(text) in my application. Before clicking on the button the textview text will always be "Start Search".But After clicking on the button , a function(scanning()) will be called. For executing the function it takes some times. So I want the texview text to be "Please wait" between the time after clicking the button and before getting the result of the function. And after getting the result of the function the textview text will be change to "Found" or "Not Found".
But problem is ,it never shows "please wait". After clicking on the button it shows "Start search" until getting the result of the function.
How to show "please wait" on the textview until it gets the result from the function after clicking on the button ??
Button button = (Button) findViewById(R.id.search);
TextView text = (TextView) findViewById(R.id.searchR);
text.setText("Start Search");
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
text.setText("Please Wait");
boolean res=scanning();
if(res==true)
text.setText("Found");
else text.setText("Not Found");
}
});
XML Part:
<Button
android:id="#+id/search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="60dp"
android:layout_marginLeft="60dp"
android:layout_marginTop="30dp"
android:text="Search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/searchR"
android:layout_width="0dp"
android:layout_height="34dp"
android:layout_marginStart="97dp"
android:layout_marginLeft="97dp"
android:layout_marginTop="30dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/search"
app:layout_constraintTop_toTopOf="parent" />
In the scanning part, I did some searching for BLE devices using UUID. If that device is found then I returned true otherwise false;
Introduce a small delay after you change the text to "Please Wait" and before calling scanning().
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
text.setText("Please Wait");
Handler delayHandler = new Handler();
delayHandler.postDelayed(new Runnable() {
#Override
public void run() {
boolean res=scanning();
if(res) {
text.setText("Found");
}
else{
text.setText("Not Found");
}
}
},100);
}
});
Edit: Depending on your code, you may need to declare your variables as static or maybe global for this to work.
I found some mistakes in your code.
You are changing text of Button instead of TextView.
public void onClick(View v) {
text.setText("Please Wait");
boolean res=scanning();
if(res==true)
but1.setText("Found");
else but1.setText("Not Found");
}
Here
but1.setText
should be replaced with
text.setText
Your scanning method is returning result very fast, that's why you will not get Please Wait text.
Write external method for change text,
for example:
public void onClick(View v)
{
changeText("Please Wait");
boolean res = scanning();
if (res) // (res == true) is unnecessary, you can use like this
changeText("Found");
else changeText("Not Found");
}
private void changeText(string s)
{
text.setText(s);
}
I got my view with SearchView component. The problem is every new symbol in the input field toggling Query action, so there are some http request are fire etc, so it starts to working slow.
I want it to run only after I click on search button on virtual keyboard (bellow button ).
Is there any properties for that in SearchView?
layout code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SearchView
android:id="#+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
local:MvxBind="Query SearchString" />
</LinearLayout>
Or I should make something like custom binding which detects keyboard key click?
UPD:
Some code to make clear part of how search works:
View model
public class RemoteMusicSearchViewModel : MvxViewModel
{
private IMvxNavigationService _mvxNavigationService;
private IRemoteMusicDataService _remoteMusicDataService;
private int _currentPage;
public RemoteMusicSearchViewModel(IMvxNavigationService mvxNavigationService,
IRemoteMusicDataService remoteMusicDataService)
{
_mvxNavigationService = mvxNavigationService;
_remoteMusicDataService = remoteMusicDataService;
}
public override void Start()
{
base.Start();
_currentPage = 0;
}
private string _searchString;
public string SearchString
{
get { return _searchString; }
set
{
_searchString = value;
RaisePropertyChanged(() => SearchString);
PerformBasicSearch().ConfigureAwait(false);
}
}
private ObservableCollection<DownloadableEntity> _foundItems;
public ObservableCollection<DownloadableEntity> FoundItems
{
get { return _foundItems; }
set
{
if (_currentPage > 0)
{
_foundItems = new ObservableCollection<DownloadableEntity>(_foundItems.Concat(value));
}
else
{
_foundItems = value;
}
RaisePropertyChanged(() => FoundItems);
}
}
private async Task PerformBasicSearch(int page = 0)
{
string request = SearchString;
string result = await _remoteMusicDataService.SearchByProperty(request, MusicSearchType.ByTracks, page).ConfigureAwait(false);
var searchResult = MusicSearchResult.FromJson(result);
await PrepareDataForOutput(searchResult).ConfigureAwait(false);
}
}
full layout code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<SearchView
android:id="#+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
local:MvxBind="Query SearchString"
/>
<MvvmCross.Binding.Droid.Views.MvxListView
android:id="#+id/searchlist"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
local:MvxBind="ItemsSource FoundItems"
local:MvxItemTemplate="#layout/listitem"/>
</LinearLayout>
I am guessing you are looking for a manual solution for the problem.
With each key event you get you can fire a delayed action after a certain threshold 500 milliseconds for example.
Maybe using Handler.postDelayed()
Then there three scenarios
Another event comes before that fires, so you call Handler.removeCallbacks() and fire another delayed action
Another event comes while the request is already fired, but no result yet, so you cancel the request and fire your delayed action.
An event comes while no delayed action pending or request pending, so you just fire a delayed action.
However, if you are willing to use any Reactive variant, there are more sophisticated solutions for that, by basically debouncing the events of keyboard strokes.
I found the solution - I just created new binding for that:
public class SearchViewKeyPressEventsBinding : MvxAndroidTargetBinding
{
private readonly SearchView _searchView;
private IMvxAsyncCommand _command;
public SearchViewKeyPressEventsBinding(SearchView searchView) : base(searchView)
{
_searchView = searchView;
_searchView.QueryTextSubmit += _searchView_KeyPress;
}
private void _searchView_KeyPress(object sender, SearchView.QueryTextSubmitEventArgs e)
{
try
{
if (_command != null)
{
_command.ExecuteAsync();
}
}
catch (Exception ex)
{
throw ex;
}
}
public override Type TargetType
{
get { return typeof(IMvxAsyncCommand); }
}
protected override void SetValueImpl(object target, object value)
{
try
{
_command = (IMvxAsyncCommand)value;
}
catch (Exception e)
{
Log.Error("SOME BINDER FAIL\n\t" + e.Message + "\n", "SOME BINDER FAIL\n\t" + e.Message + "\n");
throw;
}
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_searchView.QueryTextSubmit -= _searchView_KeyPress;
}
base.Dispose(isDisposing);
}
}
}
Then registred it and it worked:
registry.RegisterFactory(new MvxCustomBindingFactory<SearchView>("OnSearchSubmit", (sv) => new SearchViewKeyPressEventsBinding(sv)))
In layout:
<SearchView
android:id="#+id/search10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
local:MvxBind="Query SearchString; OnSearchSubmit OnSubmitPressCommand" />
And don't forget to set up viewmodel's property:
public IMvxAsyncCommand OnSubmitPressCommand
{
get
{
return new MvxAsyncCommand(OnSearchSubmit);
}
}
private async Task OnSearchSubmit()
{
//HideKeyboardOnSearchStart.Invoke(this, null);
await PerformBasicSearch().ConfigureAwait(false);
}
I am writing code for an inbox-like activity which has a button that leads to the messages. This button has a text field that counts how many messages are in the inbox.
My problem is that the button's text field is not changing when the number of messages changes. It is not a problem of the app not checking for updates, and the code with setText is being called with the correct number to update.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.v("onCreate", "Main");
// Checking if there is login
if (ParseUser.getCurrentUser() == null) {
navigateToLogin();
} else {
// Setting pointers for buttons.
// onClick methods follow.
askButton = (Button) findViewById(R.id.buttonAsk);
ansButton = (Button) findViewById(R.id.buttonAnswer);
inboxButton = (Button) findViewById(R.id.buttonCenter);
mUser = ParseUser.getCurrentUser();
updateInbox();
}
This is the method that checks for new messages and updates the button.
private void updateInbox() {
Log.v(TAG, "Updating inbox");
ParseQuery responses = new ParseQuery(ParseConstants.CLASS_ANSWER);
responses.whereMatches(ParseConstants.KEY_SENDER_ID, mUser.getObjectId());
try {
responsesCount = responses.count();
Log.v("responses count" ,""+responsesCount);
if (responsesCount > 0) {
inboxButton.setText(String.valueOf(responsesCount));
}
Log.v("InboxActivity","Label set to " + responsesCount);
} catch (ParseException e) {
Log.v("InboxActivity", e.getMessage());
}
}
updateInbox gets called correctly and in the correct moments, so I only added its code to make this as clean as possible. Here is the 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="#0099cc"
tools:context=".MainActivity"
android:clickable="false"
>
<Button
android:layout_width="80sp"
android:layout_height="80sp"
android:id="#+id/buttonCenter"
android:layout_gravity="center"
android:layout_centerInParent="true"
android:textSize="30sp"
android:text=""
android:textColor="#color/black_overlay"
android:background="#drawable/greenbutton"/>
EDIT:
Hi all, thanks for the help. I figured out the problem and posted it as an answer. It was a logical error, nothing to do with Android.
try this:
inboxButton.post(new Runnable(){
#Override
public void run(){
inboxButton.setText(String.valueOf(responsesCount));
}
});
(responsesCount should be final)
Try below code
import android.widget.RemoteViews;
if (responsesCount > 0) {
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.my_layout); //where my_layout is the layout file where the button resides.
remoteViews.setTextViewText(R.id.button, "Set button text here");// where button is id of the button.
}
findViewById doesn't exist for a widget.
Below code will work for you
runOnUiThread(new Runnable() {
#Override
public void run() {
if (responsesCount > 0) {
inboxButton.setText(String.valueOf(responsesCount));
}
}
});
You may use AsyncTask for smoothness of app flow.
I realized that the text was only getting updated when the number of responses was > 0. The button was updating correctly otherwise so I had to put an else statement to make sure the button always got updated. I edited the question with the correct answer in comments.
responsesCount = responses.count();
Log.v("responses count" ,""+responsesCount);
if (responsesCount > 0) {
inboxButton.setText(String.valueOf(responsesCount));
///////////////////////////////////
// MY EDIT HERE
//} else{
// inboxButton.setText("");
///////////////////////////////////
I know something similar has already been asked, but I'm having some trouble to get a decimal number that come from keyboard.
My Java code in the onCreate method should be:
textS0 = (EditText)findViewById(R.id.editS0);
Button btn_S0 = (Button)findViewById(R.id.getS0);
btn_S0.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
//how should I get here the number from keyboard?
//I think I should be something like
//double S0=textS0.getText()....
}
});
And that's what my XML file contains
<EditText
android:id="#+id/editS0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:hint="#string/S0"
android:inputType="numberDecimal" />
<Button
android:id="#+id/getS0"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/setS0" />
</LinearLayout>
Just do this:
double S0 = Double.parseDouble(textS0.getText().toString());
textS0=(EditText)findViewById(R.id.editS0);
Button btn_S0=(Button)findViewById(R.id.getS0);
btn_S0.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
double S0 = Double.parseDouble(textS0.getText().toString());
}
});
May b you face null pointer exception in string so try this....
get the number decimal from edittext using this
Double BText=ParseDouble(String.valueOf(edittext.getText()));
after that paste this code.. it prevents you from null pointer exception
double ParseDouble(String strNumber) {
if (strNumber != null && strNumber.length() > 0) {
try {
return Double.parseDouble(strNumber);
} catch(Exception e) {
return -1;
}
}
else return 0;
}
Quite new to coding for android but this issue has me tearing my hair out because it seems to make no sense at all...
I have an activity with four form elements in the layout: a CheckBox, two EditTexts and a Button.
When the user presses the button, it saves the content of the EditTexts as two preference values.
When the user presses the checkbox, it does the following:
If the checkbox is checked, load the preferences and store them into two variables.
Check if either of those variables contain empty strings after trimming them.
If so, show an error message, otherwise show a success message.
Essentially, the two text fields are used to set a pair of preferences which must not be empty when the checkbox is clicked.
It seems to work fine if I click the checkbox before pressing the button - error message or success message shown as appropriate.
If I press the save button and then click the checkbox, it always shows the success message regardless of the preferences.
Code follows (trimmed from the program as a whole)...
layout.xml
<CheckBox android:id="#+id/cboxActive" android:text="Click me!" android:onClick="toggleActive" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<EditText android:id="#+id/editFrom" android:layout_width="fill_parent" android:layout_height="wrap_content" android:inputType="phone"><requestFocus /></EditText>
<EditText android:id="#+id/editTo" android:layout_width="fill_parent" android:layout_height="wrap_content" android:inputType="phone"></EditText>
<Button android:id="#+id/btnSave" android:onClick="savePrefs" android:text="Save" android:layout_width="120dp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" />
</LinearLayout>
main class:
public class AutoMessengerActivity extends Activity
{
SharedPreferences settings;
CheckBox cboxActive;
EditText editFrom, editTo;
boolean active;
String from, to;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
editFrom = (EditText) findViewById(R.id.editFrom);
editTo = (EditText) findViewById(R.id.editTo);
showPrefsInUI();
}
private void loadPrefs()
{
//Load preferences
settings = getPreferences(MODE_PRIVATE);
from = settings.getString("from", "");
to = settings.getString("to", "");
}
private void showPrefsInUI()
{
loadPrefs();
//Set UI elements to preference values
editFrom.setText(from);
editTo.setText(to);
}
public void savePrefs(View view)
{
SharedPreferences.Editor editor = settings.edit();
editor.putString("from", editFrom.getText().toString());
editor.putString("to", editTo.getText().toString());
editor.commit();
Toast.makeText(this, "Prefs saved!", Toast.LENGTH_SHORT).show();
}
public void toggleActive(View view)
{
if (cboxActive.isChecked())
{
loadPrefs();
//This toast is for debugging
//It shows the correct data in all circumstances...
Toast.makeText(this, "F: " + from + " T: " + to, Toast.LENGTH_SHORT).show();
//This is the part that seems to fail if you save then click checkbox
if (from.trim() == "" || to.trim() == "")
{
Toast.makeText(this, "Error - Prefs not saved", Toast.LENGTH_LONG).show();
cboxActive.setChecked(false);
}
else
{
Toast.makeText(this, "Success!", Toast.LENGTH_SHORT).show();
}
}
else
{
Toast.makeText(this, "Unchecked!", Toast.LENGTH_SHORT).show();
}
}
}
Hopefully that code gives an idea of the problem and allows it to be replicated...
Oh god, how silly of me - Urban and jcxavier hit the nail on the head... I forgot about that damn annoying quirk of Java! Changed the line to
from.trim().equals("") || to.trim().equals("")
And it works fine!
For what it's worth, that HAD actually crossed my mind briefly, but it was 2am when I tried equals and I got confused about requiring an Object as the parameter and ended up specifying null rather than "" - which didn't work...