SlidingPaneLayout: slide without smooth animation? - android

I'm using the new SlidingPaneLayout available in the latest support library, which provides the openPane() and closePane() methods to smooth open and close the panel. Unfortunately, there are no public methods to do so without animation.
Is there a way to still do this? I have a feeling reflection may be necessary.
P.S. The file is available under sdk/extras/android/support/v4/src/java/android/support/v4/widget/.

I ended up writing a subclass that provides two methods, openPaneNoAnimation() and closePaneNoAnimation(). Yes, it's reflection, and may stop working with future support libraries, but for now it does the job. Worst case, the methods fall back to using openPane() and closePane().
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.content.Context;
import android.support.v4.widget.SlidingPaneLayout;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class AnimationlessSlidingPaneLayout extends SlidingPaneLayout {
private boolean mSlideEnabled = true;
private Field mSlideOffsetField = null;
private Field mSlideableViewField = null;
private Method updateObscuredViewsVisibilityMethod = null;
private Method dispatchOnPanelOpenedMethod = null;
private Method dispatchOnPanelClosedMethod = null;
private Field mPreservedOpenStateField = null;
private Method parallaxOtherViewsMethod = null;
public AnimationlessSlidingPaneLayout(Context context) {
this(context, null, 0);
}
public AnimationlessSlidingPaneLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AnimationlessSlidingPaneLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
try {
mSlideOffsetField = SlidingPaneLayout.class.getDeclaredField("mSlideOffset");
mSlideableViewField = SlidingPaneLayout.class.getDeclaredField("mSlideableView");
updateObscuredViewsVisibilityMethod = SlidingPaneLayout.class.getDeclaredMethod("updateObscuredViewsVisibility",
View.class);
dispatchOnPanelClosedMethod = SlidingPaneLayout.class.getDeclaredMethod("dispatchOnPanelClosed", View.class);
dispatchOnPanelOpenedMethod = SlidingPaneLayout.class.getDeclaredMethod("dispatchOnPanelOpened", View.class);
mPreservedOpenStateField = SlidingPaneLayout.class.getDeclaredField("mPreservedOpenState");
parallaxOtherViewsMethod = SlidingPaneLayout.class.getDeclaredMethod("parallaxOtherViews", float.class);
mSlideOffsetField.setAccessible(true);
mSlideableViewField.setAccessible(true);
updateObscuredViewsVisibilityMethod.setAccessible(true);
dispatchOnPanelOpenedMethod.setAccessible(true);
dispatchOnPanelClosedMethod.setAccessible(true);
mPreservedOpenStateField.setAccessible(true);
parallaxOtherViewsMethod.setAccessible(true);
} catch (Exception e) {
Log.w("ASPL", "Failed to set up animation-less sliding layout.");
}
}
public void openPaneNoAnimation() {
try {
View slideableView = (View) mSlideableViewField.get(this);
mSlideOffsetField.set(this, 1.0f);
parallaxOtherViewsMethod.invoke(this, 1.0f);
requestLayout();
invalidate();
dispatchOnPanelOpenedMethod.invoke(this, slideableView);
mPreservedOpenStateField.set(this, true);
} catch (Exception e) {
openPane();
}
}
public void closePaneNoAnimation() {
try {
View slideableView = (View) mSlideableViewField.get(this);
mSlideOffsetField.set(this, 0.0f);
parallaxOtherViewsMethod.invoke(this, 0.0f);
requestLayout();
invalidate();
updateObscuredViewsVisibilityMethod.invoke(this, slideableView);
dispatchOnPanelClosedMethod.invoke(this, slideableView);
mPreservedOpenStateField.set(this, false);
} catch (Exception e) {
closePane();
}
}
}

Related

Android Ripple Effect for my custom button

I have created a project with custom button class as below.
package app.kibbeh.widget;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Button;
public class MyFontButton extends Button {
private static final String TAG = "TextView";
private Typeface typeface;
public MyFontButton(Context context) {
super(context);
}
public MyFontButton(Context context, AttributeSet attrs) {
super(context, attrs);
setCustomFont(context, "");
}
public MyFontButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setCustomFont(context, "");
}
private boolean setCustomFont(Context ctx, String asset) {
try {
if (this.typeface == null) {
Log.i(TAG, "asset:: fonts/" + asset);
this.typeface = Typeface.createFromAsset(ctx.getAssets(), "fonts/avenirroman.ttf");
}
setTypeface(this.typeface);
return true;
} catch (Exception e) {
e.printStackTrace();
Log.e(TAG, "Could not get typeface: " + e.getMessage());
return false;
}
}
}
Now i have to add ripple effect from here. so is it possible or i have to add rippelview in all xml file ? please guide me.
Thank you..
try this link for set the ripple effect on button:
Material effect on button with background color
Android L's Ripple Effect - Touch Feedback for Buttons - Using XML

I can't see visualizer while recording voice on Android

Update : I unfortunately can't get a solution for my question yet...
I am developing a recorder on android and I want show visualize while recording. I have bellow classes.
When I run project and press start record I cant see any visualizer while recording.I test bellow code on real device and add permissions to manifest.
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
I use from open source project visualizer for playing by "Felix Palmer" and own add new method for link recorder to visualizer. I think must be run perfectly.but really any showing for my recorder unfortunately. (code has not any error)
How i can resolve my problem and see visualizer?
This is VisualizerView.java class...
import java.util.HashSet;
import java.util.Set;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.media.audiofx.Visualizer;
import android.util.AttributeSet;
import android.view.View;
import com.pheelicks.visualizer.renderer.Renderer;
public class VisualizerView extends View {
private byte[] mBytes;
private byte[] mFFTBytes;
private Rect mRect = new Rect();
private Visualizer mVisualizer;
private Set<Renderer> mRenderers;
private Paint mFlashPaint = new Paint();
private Paint mFadePaint = new Paint();
public VisualizerView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs);
init();
}
public VisualizerView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public VisualizerView(Context context)
{
this(context, null, 0);
}
private void init() {
mBytes = null;
mFFTBytes = null;
mFlashPaint.setColor(Color.argb(122, 255, 255, 255));
mFadePaint.setColor(Color.argb(238, 255, 255, 255)); // Adjust alpha to change how quickly the image fades
mFadePaint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
mRenderers = new HashSet<Renderer>();
}
//-----------------------------------------------
public void link(MediaRecorder recorder)
{
if(recorder == null)
{
throw new NullPointerException("Cannot link to null MediaPlayer");
}
mVisualizer = new Visualizer(0);
mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
Visualizer.OnDataCaptureListener datacaptureListener1=new Visualizer.OnDataCaptureListener()
{
#Override
public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate)
{
// TODO Auto-generated method stub
updateVisualizer(bytes);
}
#Override
public void onFftDataCapture(Visualizer visualizer, byte[] bytes,
int samplingRate)
{
// TODO Auto-generated method stub
updateVisualizerFFT(bytes);
}
};
mVisualizer.setDataCaptureListener(datacaptureListener1,Visualizer.getMaxCaptureRate() /2,false,true);
mVisualizer.setEnabled(true);
}
//-----------------------------------------------
This is AudioRecorder.java...
public class AudioRecorder
{
private String name="";
private static int id=0;
private MediaRecorder recorder = new MediaRecorder();
private String path=null;
VisualizerView mVisualizerView;
private Context context=null;
//-----------------------------------------------
public AudioRecorder(Context context)
{
this.context=context;
}
//-----------------------------------------------
public void Record() throws IOException
{
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(this.path);
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.recorder1, null);
mVisualizerView = (VisualizerView)view.findViewById(R.id.visualizer1);
mVisualizerView.link(recorder);
addLineRenderer();
try
{
recorder.prepare();
}
catch (IllegalStateException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
try
{
System.out.println("****");
recorder.start();
}
catch (Exception e)
{
}
}
//------------------------------------------
public void stopRecord() throws IOException
{
recorder.stop();
recorder.release();
}
And this is visualizer in my XML.
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:background="#drawable/highlight_shared"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="5dp" >
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#drawable/corner_liner" >
<com.pheelicks.visualizer.VisualizerView
android:id="#+id/visualizer1"
android:layout_width="match_parent"
android:layout_height="fill_parent"/>
</FrameLayout>
</LinearLayout>
It is the issue with certain phones for different reasons. You can see here
https://code.google.com/p/android/issues/detail?id=64423
https://github.com/felixpalmer/android-visualizer/issues/5
for the list of unsupported devices.

How do I draw a drawable with its exact size in my custom view?

SOLVED: Solution below as answer.
I have a custom view with a TransitionDrawable and when I draw it in the onDraw() method it scales automatically to fill the whole parent layout, even when it's set in the xml to wrap_content. The picture is in mdpi and hdpi and my testing device (samsung galaxy s) I think it's no more than hdpi.
package com.adyrsoft.pronunciationtrainer;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.TransitionDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class RecordButton extends View {
private static final String TAG = "RecordButton";
private TransitionDrawable mDrawable;
private boolean mActivated;
private OnClickListener mOnClickListenerInternal = new OnClickListener() {
#Override
public void onClick(View v) {
toggleState();
if(mOnClickListener != null) {
mOnClickListener.onClick(v);
}
}
};
private OnClickListener mOnClickListener = null;
public RecordButton(Context context) {
super(context);
init();
}
public RecordButton(Context context, AttributeSet attrib) {
super(context, attrib);
init();
}
public RecordButton(Context context, AttributeSet attrib, int defStyle) {
super(context, attrib, defStyle);
init();
}
public void setState(boolean activated) {
mActivated = activated;
if(mActivated){
mDrawable.startTransition(300);
}
else {
mDrawable.reverseTransition(300);
}
}
public void toggleState() {
if(mActivated) {
setState(false);
}
else {
setState(true);
}
}
#SuppressWarnings("deprecation")
private void init() {
mActivated = false;
mDrawable = (TransitionDrawable) getResources().getDrawable(R.drawable.btnrecord);
Log.d(TAG, "Drawable intrinsic width and height are: " +
Integer.toString(mDrawable.getIntrinsicWidth()) + " " +
Integer.toString(mDrawable.getIntrinsicHeight()));
mDrawable.setBounds(0,0,mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight());
Log.d(TAG, "The bounds for the button are: "+mDrawable.getBounds().flattenToString());
super.setBackgroundDrawable(mDrawable);
setClickable(true);
super.setOnClickListener(mOnClickListenerInternal);
invalidate();
}
public void setOnClickListener(View.OnClickListener listener) {
mOnClickListener = listener;
}
protected void onDraw (Canvas canvas) {
super.onDraw(canvas);
}
}
What am I doing wrong?
Thanks.
After hours trying to understand how I should use the drawables in a custom view in order to be displayed in its original size, I've figured out how to do it.
First a few things that I didn't know but are a must is:
The background drawable should be left to the parent class to be
drawn when using View as the parent. If not, the TransitionDrawable can't be seen fading between pictures.
Only if I am going to draw on the background drawable I should override onDraw() and do the drawing there.
And the last but not less important is that I should override onMeasure() to specify the size of the view. If I don't do it, it will fill all the free space in the parent layout, as it was happening to me.
I've passed the TransitionDrawable to the parent class with setBackgroundDrawable() and since I wasn't drawing in the background drawable, I've removed the onDraw() method. Also I've implemented onMeasure() with a quick and dirty solution specifying the size of the picture I am drawing.
This is the final result:
public class RecordButton extends View {
private static final String TAG = "RecordButton";
private static final int DESIRED_WIDTH = 180;
private static final int DESIRED_HEIGHT = 66;
private TransitionDrawable mDrawable;
private Rect mViewRect;
private boolean mActivated;
private OnClickListener mOnClickListenerInternal = new OnClickListener() {
#Override
public void onClick(View v) {
toggleState();
if(mOnClickListener != null) {
mOnClickListener.onClick(v);
}
}
};
private OnClickListener mOnClickListener = null;
public RecordButton(Context context) {
this(context, null, 0);
}
public RecordButton(Context context, AttributeSet attrib) {
this(context, attrib, 0);
}
public RecordButton(Context context, AttributeSet attrib, int defStyle) {
super(context, attrib, defStyle);
init();
}
public void setState(boolean activated) {
mActivated = activated;
if(mActivated){
mDrawable.startTransition(300);
}
else {
mDrawable.reverseTransition(300);
}
}
public void toggleState() {
if(mActivated) {
setState(false);
}
else {
setState(true);
}
}
#SuppressWarnings("deprecation")
private void init() {
mActivated = false;
mDrawable = (TransitionDrawable) getResources().getDrawable(R.drawable.btnrecord);
setBackgroundDrawable(mDrawable);
setClickable(true);
super.setOnClickListener(mOnClickListenerInternal);
invalidate();
}
public void setOnClickListener(View.OnClickListener listener) {
mOnClickListener = listener;
}
#Override
protected void onMeasure(int m, int n) {
setMeasuredDimension(DESIRED_WIDTH, DESIRED_HEIGHT);
}
}

Android library component

I'm trying to make a reusable component that slides its content.
Here is the code of the component :
package com.example.components;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.LinearLayout;
public class SlideRightNLeft extends LinearLayout{
private LinearLayout.LayoutParams params1;
private LinearLayout.LayoutParams params2;
public SlideRightNLeft(Context context) {
super(context);
}
public SlideRightNLeft(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SlideRightNLeft(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void slideRight(){
int x = 0;
while(x < 300) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
params1 = new LinearLayout.LayoutParams(this.getChildAt(0).getWidth(), this.getChildAt(0).getHeight());
params1.setMargins(300 - x, 1, 1, 1);
params2 = new LinearLayout.LayoutParams(this.getChildAt(0).getWidth(), this.getChildAt(0).getHeight());
params2.setMargins(x, 1, 1, 1);
this.getChildAt(0).setLayoutParams(params1);
this.getChildAt(1).setLayoutParams(params2);
x++;
this.invalidate();
}
}
}
Unfortunately it won't slide. In can suppose I have to use runOnUiThread to actually see it slide, but it needs an activity, and as I am in the component, I don't have access to it.
You can use the Context thats passed into the constructor to access the runOnUiThread method.
((Activity)context).runOnUiThread(new Runnable(){
});

Add button to a SlidingDrawer handle?

I've been working on this for a while. The Idea started simple, I wanted a button on a SlidingDrawer handle to allow the user to view settings specific to the content of the drawer. So I made a layout with a button off to the side and set it as the handle. The drawer drew fine, but would not allow the button (on the handle) to be pressed. When ever I try to click the thing, the click is interpreted as a handle click, and toggle the state of the drawer.
Does anyone know whats going on?
Thanks ~Aedon
I'll post my implementation, to save others the trouble.
You basically have to extend the SlidingDrawer class and handle the onInterceptTouch events to pass through when they're on top of items inside the handle layout.
This assumes you are using a ViewGroup (e.g. any layout) for the handle and all the views inside it are clickable.
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SlidingDrawer;
public class ClickableSlidingDrawer extends SlidingDrawer {
private ViewGroup mHandleLayout;
private final Rect mHitRect = new Rect();
public ClickableSlidingDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public ClickableSlidingDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
View handle = getHandle();
if (handle instanceof ViewGroup) {
mHandleLayout = (ViewGroup) handle;
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mHandleLayout != null) {
int childCount = mHandleLayout.getChildCount();
int handleClickX = (int)(event.getX() - mHandleLayout.getX());
int handleClickY = (int)(event.getY() - mHandleLayout.getY());
Rect hitRect = mHitRect;
for (int i=0;i<childCount;i++) {
View childView = mHandleLayout.getChildAt(i);
childView.getHitRect(hitRect);
if (hitRect.contains(handleClickX, handleClickY)) {
return false;
}
}
}
return super.onInterceptTouchEvent(event);
}
}
Then, in your layout .xml just use <my.package.name.ClickableSlidingDrawer> instead of <SlidingDrawer>
I tried out d4n3's implementation, but since my handle contains a button that is nested within multiple ViewGroups, I had to modify it to make it work.
My implementations also assumes that you are using a ViewGroup for the handle, but the child views don't have to be clickable. Also, you have to set the tag to "click_intercepted" of the View(s) that you want to be clickable in the handle. Only child views with this specific tag set will be considered for clicks within the handle. This way, you can layout your handle anyway you want, and still act appropriately on clicks on specific Views (e.g. a Button) in the handle. Also, with this implementation, you can still both drag and click the handle to toggle its state.
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SlidingDrawer;
public class ClickableSlidingDrawer extends SlidingDrawer
{
private static final String TAG_CLICK_INTERCEPTED = "click_intercepted";
private ViewGroup mHandleLayout;
private final Rect mHitRect = new Rect();
public ClickableSlidingDrawer(Context context, AttributeSet attrs)
{
super(context, attrs);
}
public ClickableSlidingDrawer(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
#Override
protected void onFinishInflate()
{
super.onFinishInflate();
View handle = getHandle();
if (handle instanceof ViewGroup)
{
mHandleLayout = (ViewGroup) handle;
}
}
#Override
public boolean onInterceptTouchEvent(MotionEvent event)
{
if (mHandleLayout != null)
{
int clickX = (int) (event.getX() - mHandleLayout.getLeft());
int clickY = (int) (event.getY() - mHandleLayout.getTop());
if (isAnyClickableChildHit(mHandleLayout, clickX, clickY))
{
return false;
}
}
return super.onInterceptTouchEvent(event);
}
private boolean isAnyClickableChildHit(ViewGroup viewGroup, int clickX, int clickY)
{
for (int i = 0; i < viewGroup.getChildCount(); i++)
{
View childView = viewGroup.getChildAt(i);
if (TAG_CLICK_INTERCEPTED.equals(childView.getTag()))
{
childView.getHitRect(mHitRect);
if (mHitRect.contains(clickX, clickY))
{
return true;
}
}
if (childView instanceof ViewGroup && isAnyClickableChildHit((ViewGroup) childView, clickX, clickY))
{
return true;
}
}
return false;
}
}
You can suppress the action that interprets a click on the handle button as an "open" with an attribute in the SlidingDrawer element in the layout XML. Like this:
<SlidingDrawer android:layout_width="fill_parent"android:id="#+id/SlidingDrawer" android:handle="#+id/slideHandleButton"
android:content="#+id/txtHolder" android:layout_height="fill_parent"
android:orientation="horizontal" android:allowSingleTap="false">
Just make the android:allowSingleTap="false" Then just implement a click handler for the button like you normally would. This will stop it from opening/closing the drawer, but you might need to intercept the events for the button to get it to do what YOU want it to do.
First make a layout and put your Handle content in it (say you put in handle_content.xml).
Second replace your current handle handle with this:
<include android:id="#id/handle"
android:layout="#layout/handle_content.xml"/>
Now do as below (I say this because below work correctly if u do as above)
This is my implementation:
package com.examples.my.views;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.SlidingDrawer;
import com.examples.my.MainFragmentActivity;
public class MYSlidingDrawer extends SlidingDrawer {
private View button;
private int height;
private MainFragmentActivity activity;
public MYSlidingDrawer (Context context, AttributeSet attrs) {
super(context, attrs);
DisplayMetrics metrics = this.getResources().getDisplayMetrics();
height = metrics.heightPixels;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
int left = button.getLeft();
int top = button.getTop();
int right = button.getRight();
int bottom = button.getBottom();
Rect rect = new Rect(left, top, right, bottom);
int x = (int) event.getX();
int y = (int) event.getY();
if (isOpened()) {
if (rect.contains(x, y)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (activity != null)
{
//HERE DO YOUR WORK
// Like activity.tooglePlay();
}
}
return true;
}
} else {
y -= height;
if (rect.contains(x, Math.abs(y))) {
if (event.getAction() == MotionEvent.ACTION_UP) {
if (activity != null)
{
//HERE DO YOUR WORK
// Like activity.tooglePlay();
}
}
return true;
}
}
return super.onTouchEvent(event);
}
public void setButton(View button) {
this.button = button;
}
public void setActivity(MainFragmentActivity activity) {
this.activity = activity;
}
}
And now define this in which you include MYSlidingDrawer:
MYSlidingDrawer drawer = (MYSlidingDrawer) findViewById(R.id.drawer);
drawer.setActivity(this);
Button btn = (Button) findViewById(R.id.play_btn);//button inside your handle
drawer.setButton(btn);
Hope this help you.

Categories

Resources