Prevent Android SeekBar of drawing multiple thumbs on progress change - android

I'm creating an overlay dialog for my appplication, this is how it looks:
The XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="#+id/ipTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="0"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#android:color/white" />
<SeekBar
android:id="#+id/volumeSeekBar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_weight="1"
android:focusable="false"
android:indeterminate="false"
android:max="63"
android:mirrorForRtl="false" />
<TextView
android:id="#+id/portTv"
style="#android:style/Theme.Dialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="63"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#android:color/white" />
</LinearLayout>
The code:
if (alertDialogLayout == null) {
alertDialogLayout = View.inflate(getApplicationContext(), R.layout.volume_dialog, null);
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
WindowManager.LayoutParams wmlp = new LayoutParams();
wmlp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
wmlp.width = dm.widthPixels / 5;
wmlp.height = LayoutParams.WRAP_CONTENT;
wmlp.y += 10;
wmlp.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
wmlp.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL;
wm.addView(alertDialogLayout, wmlp);
}
if (volumeBar == null) {
volumeBar = (SeekBar) alertDialogLayout.findViewById(R.id.volumeSeekBar);
volumeBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//Do some stuff
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) { }
#Override
public void onStopTrackingTouch(SeekBar seekBar) { }
});
}
Why the SeekBar adds more than one thumb? As more I drag it, more thumbs appear, until it's completely full with them. I've checked - OnSeekBarChangeListener is assigned only once. Is there any errors in the code/layout, which I'm missing and causing such strange visual behaviour?
Thanks!

I think your problem is not multiple thumbs, but multiple SeekBars, multiple alertDialogLayout and each one has a SeekBar, or multiple objects created each one creates an alertDialogLayout, So, at first make sure that you initiate the alertDialogLayout only once and also that you initiate the SeekBar only once, also make sure that you are not (some how) initiate an alertDialogLayout or a SeekBar every SeekBar progress change, and to momentarily test all of that, just change the alertDialogLayout and the SeekBar declaration to static and look if that strange behaviour disappears.

Related

DialogFragment: constant height of the central view

I have a DialogFragment which consists of three parts, from up to down: the title, the central view which displays all the contents, and the bottom pane which holds the PositiveButton "OK":
public Dialog onCreateDialog(Bundle savedInstanceState)
{
FragmentActivity act = getActivity();
LayoutInflater inflater = act.getLayoutInflater();
AlertDialog.Builder builder = new AlertDialog.Builder(act);
// TITLE:
TextView title = (TextView) inflater.inflate(R.layout.dialog_title, null);
title.setText(R.string.updates);
builder.setCustomTitle(title);
// CENTRAL VIEW:
View view = inflater.inflate(R.layout.dialog_updates, null);
// ... customize it ...
builder.setView(view);
// POSITIVE BUTTON:
builder.setPositiveButton( R.string.ok, new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// something
}
});
}
The stuff that's shown by the central view is downloaded from the web. Initially, when a user pops up the dialog, the View shows just the "Downloading..." message:
When we get an answer, we create a ScrollView and keep adding vertically scrollable Panes to it like so:
(image above shows three such panes added so far)
The result is that the height of the dialog keeps changing, which is visually unpleasant.
So I really want to keep the height of the whole Dialog constant, let's say pinned to 3/4 of the height of the screen. Let's do it then:
public void onResume()
{
super.onResume();
Window window = getDialog().getWindow();
Context context = getContext();
if( window!=null && context!=null )
{
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
final float height= metrics.heightPixels;
WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = (int)(0.75f*height);
window.setAttributes(params);
}
}
Result:
This does kind of work, as you can see though - it works by enlarging the lower pane with the 'OK' button, rather than the central View.
How to fix this?
EDIT: here's my dialog_title.xml:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textSize="20sp"
android:gravity="center"
android:padding="10dp"/>
One workaround for this issue is to use ConstrainedLayout for your whole dialog like this:
fragment_dialog layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="Updates"
android:textSize="20sp"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/central_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:text="Downloading"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="#id/positive_action"
app:layout_constraintHeight_percent="0.8"
app:layout_constraintTop_toBottomOf="#id/title" />
<androidx.appcompat.widget.AppCompatButton
android:id="#+id/positive_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_margin="8dp"
android:text="OK"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
You can change the percentage of your central view with app:layout_constraintHeight_percent="0.8"
DialogFragment class:
public class LoadingDialog extends DialogFragment {
#NonNull
#Override
public Dialog onCreateDialog(#Nullable Bundle savedInstanceState) {
FragmentActivity act = getActivity();
LayoutInflater inflater = act.getLayoutInflater();
View view = inflater.inflate(R.layout.fragment_dialog, null);
AlertDialog.Builder builder = new AlertDialog.Builder(act).setView(view);
// POSITIVE BUTTON:
view.findViewById(R.id.positive_action).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//something
}
});
return builder.create();
}
#Override
public void onResume() {
super.onResume();
getDialog().getWindow().setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
}
}
And you will get this result:

Showing an overlay in Android

My idea was to have a simple layout for a normal notification and a dialog control for the pop up with animation. Since the pop up needs to be at the point on which the user clicks, I wanted to write a custom DialogueView extending the Dialog class, within which I determine the anchor point.
This is what I have done so far:
Design of the layout without the pop up:
pre_session_view_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:verizon="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:id="#+id/preSessionLayout">
<TextView
android:id="#id/verizon_sd_caption"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_toLeftOf="#id/verizon_sd_empty_view"
android:layout_toRightOf="#id/verizon_sd_icon"
android:gravity="center|center_horizontal"
android:paddingLeft="5dp"
android:paddingRight="10dp"
android:textColor="#android:color/white"
android:textSize="18sp"></TextView>
<demo.notification.verizon.com.notificationdemo.ResizableImageView
android:id="#id/verizon_sd_icon"
android:layout_width="36dp"
android:layout_height="34dp"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
android:scaleType="centerInside" />
<!--android:src="#drawable/_fw_prenotify" -->
<ImageView
android:id="#id/verizon_sd_empty_view"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:visibility="invisible"
/>
</RelativeLayout>
The design of the overlay:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="240dp"
android:layout_height="110dp"
android:orientation="vertical"
android:id="#+id/overlayLayout">
<ImageView
android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/notification_bg_green"/>
</RelativeLayout>
PressionView.java: ( Java file for showing the normal and pop up notification)
I am only giving the code snippet for showing the pop up as an overlay.This function gets called when the user clicks on the bee icon.
private void showOverLay(){
final ConfirmBox dialog = new ConfirmBox(this.bannerThumb);
LayoutInflater inflater = LayoutInflater.from(ctx);
dialog.onCreateView(inflater,this.relLayout,null);
}
Finally the custom Dialogue is as follows:
public class ConfirmBox extends DialogFragment {
private View source;
public ConfirmBox() {
}
public ConfirmBox(View source) {
this.source = source;
}
public static ConfirmBox newInstance(View source) {
return new ConfirmBox(source);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(STYLE_NO_FRAME, R.style.AppTheme);
}
#Override
public void onStart() {
super.onStart();
// Less dimmed background; see http://stackoverflow.com/q/13822842/56285
Window window = getDialog().getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.dimAmount = 0.2f; // dim only a little bit
window.setAttributes(params);
// Transparent background; see http://stackoverflow.com/q/15007272/56285
// (Needed to make dialog's alpha shadow look good)
window.setBackgroundDrawableResource(android.R.color.transparent);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Put your dialog layout in R.layout.view_confirm_box
View view = inflater.inflate(R.layout.overlay_view, container, false);
// Initialise what you need; set e.g. button texts and listeners, etc.
// ...
setDialogPosition();
return view;
}
/**
* Try to position this dialog next to "source" view
*/
private void setDialogPosition() {
if (source == null) {
return; // Leave the dialog in default position
}
// Find out location of source component on screen
// see http://stackoverflow.com/a/6798093/56285
int[] location = new int[2];
source.getLocationOnScreen(location);
int sourceX = location[0];
int sourceY = location[1];
Window window = getDialog().getWindow();
// set "origin" to top left corner
window.setGravity(Gravity.TOP| Gravity.LEFT);
WindowManager.LayoutParams params = window.getAttributes();
// Just an example; edit to suit your needs.
params.x = sourceX - dpToPx(110); // about half of confirm button size left of source view
params.y = sourceY - dpToPx(80); // above source view
window.setAttributes(params);
}
public int dpToPx(float valueInDp) {
DisplayMetrics metrics = getActivity().getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, valueInDp, metrics);
}
}
I can show the normal notification. But for the pop up , the approach which I have taken,I get a null pointer exception in the showOverLay() method.
More specifically on the line:
Window window = getDialog().getWindow();
within the getDialoguePosition() of the custom Dialogueclass.
Can someone kindly help me on this? If reqd I can share the full code for the PresessionView.java file as well.
I think I am making some mistake in calling the custom Dialogue class.
Thanks.
You show the dialog fragment incorrect.
Please use dialog.show(...). Also please ensure that you're bannerThumb, ctx, and relLayout are not null.
private void showOverLay(){
final ConfirmBox dialog = new ConfirmBox(this.bannerThumb);
LayoutInflater inflater = LayoutInflater.from(ctx); //Ensure ctx is not NULL
dialog.show(getSupportFragmentManager(), "yourtitle");
}

Dialog custom layout not stretching properly

Im creating a custom Dialog.
But it is showing extra space around.
Code:
private void showPushAlert(Context context, String message, int layoutID) {
// custom dialog
final Dialog dialog = new Dialog(context);
dialog.setContentView(layoutID);
TextView tvPushMessage = (TextView) dialog.findViewById(R.id.tvAlertMessage);
tvPushMessage.setText(message);
Button btnPushOk = (Button) dialog.findViewById(R.id.btnAlertOk);
btnPushOk.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view) {
dialog.dismiss();
}
});
dialog.show();
}
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="275dp"
android:layout_height="wrap_content"
android:background="#drawable/background_round_rectangle"
android:orientation="vertical"
android:padding="#dimen/activity_vertical_margin">
<TextView
android:id="#+id/tvAlertMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="Message"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#color/black" />;
<Button
android:id="#+id/btnAlertOk"
android:layout_width="65dp"
android:layout_height="30dp"
android:layout_below="#id/tvAlertMessage"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:background="#drawable/btn_use"
android:text="#string/ok"
android:textColor="#color/white" />
</RelativeLayout>
I also tried inflater:
final Dialog dialog = new Dialog(context);
View view = getLayoutInflater().inflate(layoutID, null);
dialog.setContentView(view);
But not the perfect result. Jut the width stretched.
I wanted to keep simple, so used just Dialog, instead of AlertDialog, or Dialog fragment.
Not sure why that was happening..
Used https://stackoverflow.com/a/6922903/4510869
to do this after dialog.show()
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = 500;
dialog.getWindow().setAttributes(lp);
This showed the custom width, but the top space was still visible.
Had to change to AlertDialog.Builder + the above snippet (AlertDialog also showed top space) to finally get the result.

How do I put a seek bar in an alert dialog?

I want an alert dialog box to pop up after I click a button to have a seek bar so that the person can change the value from 1-48. I've made a custom xml that has a seek bar and two text view and I would like the dialog box to have two buttons, one just cancels the dialog and the other does more work. Here is the code I have so far.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<SeekBar android:max="48" android:layout_height="wrap_content" android:id="#+id/seekBar1" android:layout_width="fill_parent" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_marginTop="74dp"></SeekBar>
<TextView android:text="Change Hour Range" android:layout_height="wrap_content" android:id="#+id/textView1" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_width="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true"></TextView>
<TextView android:text="Only most recent positions" android:layout_height="wrap_content" android:id="#+id/textView2" android:layout_width="wrap_content" android:layout_below="#+id/textView1" android:layout_centerHorizontal="true" android:layout_marginTop="15dp"></TextView>
</RelativeLayout>
Here is the alert dialog that I have so far
new AlertDialog.Builder(ImTracking.this)
//is there something like setcontentview for alert dialogs?
.setPositiveButton("Cancel", null)
.setNegativeButton("OK",
new DialogInterface.OnClickListener() {
// does some work here
Yep builder.setView(View v); here is how you can use it.
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.yourLayoutId, (ViewGroup) findViewById(R.id.yourLayoutRoot));
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setView(layout);
AlertDialog alertDialog = builder.create();
alertDialog.show();
SeekBar sb = (SeekBar)layout.findViewById(R.id.yourSeekBar);
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
//Do something here with new value
}
});
Edit: Added progressListener to sample code. Do note that you cannot get a reference to your SeekBar until after you call alertDialog.show(), if the SeekBar is not being shown findViewById() will return null. Also note that you must use layout.findViewById(); because the SeekBar is a child of the RelativeLayout that 'layout' is a reference to.
//This is the code you seek!
public void ShowDialog(){
final AlertDialog.Builder popDialog = new AlertDialog.Builder(this);
final SeekBar seek = new SeekBar(this);
seek.setMax(255);
seek.setKeyProgressIncrement(1);
popDialog.setIcon(android.R.drawable.btn_star_big_on);
popDialog.setTitle("Please Select Into Your Desired Brightness ");
popDialog.setView(seek);
seek.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser){
txtView.setText("Value of : " + progress);
}
Or you can look on this tutorial

Discrete seekbar in Android app?

I would like to create a seekbar for an Android app that allows the user to select a value between -5 and 5 (which maps to "strongly disagree" and "strongly agree"). How do I make a seekbar with discrete values? or is there a better UI widget I could use for this?
Thanks.
The Seekbar works great for discrete values. We use a Seekbar for discrete data as shown below. To show which item is selected, we just change the font of the selected text view so it is bigger. You could also highlight by changing the background color or something. It works pretty well. You will want to call setMax(11) on the seek bar, and then in your code you need to translate between the range (0 through 11) and (-5 through 5) appropriately.
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal">
<TextView android:text="-5"
android:id="#+id/answerNegative5"
android:textSize="25sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView android:text="-4"
android:id="#+id/answerNegative4"
android:textSize="25sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView android:text="-3"
android:id="#+id/answerNegative3"
android:textSize="25sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
.....
</LinearLayout>
<SeekBar android:id="#+id/intensitySlider"
android:layout_weight="0"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
If you want to implement discrete seekbar with number of gaps without using third party library then use style property of seekbar.
<SeekBar
android:id="#+id/sb"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="10"
android:thumb="#drawable/ic_location"
android:theme="#style/Widget.AppCompat.SeekBar.Discrete" />
I don't know what is the issue here, you add a seekbar having a range of 0-10. Then you can map these values to -5 if you substract -5 from the selected value.
EDIT
add android:max="10" to the xml definiton and you get a fixed size seekbar.
You maybe should consider to add a textview to denote the current selected value's textual representation such as: Strongly Disagree.
To update this view, subscribe to onProgressChanged event and progress parameter will give you the chosen number.
SeekBar s = (SeekBar) findViewById(R.id.SeekBar);
s.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar,
int progress, boolean fromUser) {
}
}
Just use the material components library with something like:
<com.google.android.material.slider.Slider
android:stepSize="1"
android:valueFrom="-5"
android:valueTo="5"
/>
I hope this code surely helpes you.
Try this...
float discrete = 0;
float start = 0;
float end = 100;
float start_pos = 0;
int start_position = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = -10; //you need to give starting value of SeekBar
end = 10; //you need to give end value of SeekBar
start_pos = 5; //you need to give starting position value of SeekBar
start_position = (int)(((start_pos - start) / (end - start)) * 100);
discrete = start_pos;
SeekBar seek = (SeekBar) findViewById(R.id.seekBar1);
seek.setProgress(start_position);
seek.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
Toast.makeText(getBaseContext(), "discrete = " + String.valueOf(discrete), Toast.LENGTH_SHORT).show();
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// TODO Auto-generated method stub
// To convert it as discrete value
float temp = progress;
float dis = end - start;
discrete = (start + ((temp / 100) * dis));
}
});
}
1-st step : set maxVal to 10;
2-nd step :
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser)
{
switch(seekBar.getId())
{
case R.id.mySeekBar:
int prValue = progress - 5;
editText.setText(String.valueOf(preValue));
break;
}
}
<SeekBar
android:id="#+id/seekbar"
style="#style/SeekBarWithoutSteps"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="22dp"
android:layout_marginRight="22dp"
android:layout_marginTop="#dimen/margin_10"
android:max="4"
android:maxHeight="#dimen/margin_5"
android:minHeight="#dimen/margin_5"
android:paddingLeft="#dimen/margin_10"
android:paddingRight="#dimen/margin_10"
android:progressBackgroundTint="#color/colorGray"
android:progressTint="#color/colorGreen"
android:theme="#style/Widget.AppCompat.SeekBar.Discrete"
android:thumb="#drawable/filled_green"
android:thumbOffset="15dp" />

Categories

Resources