问题
Background
We all know we can use a navigation drawer as a new way to navigate in an app (even with a library, like this one) .
We also know that some apps can float above others (as shown on AirCalc, and done like so) ,using a SYSTEM_ALERT_WINDOW permission.
I've noticed that some apps combine expanding & collapsing of views that are on top , like the next ones:
- callerid
- Sidebar Lite
- Easy Controller-Control Center
and many more...
The problem
We need to merge the 2 concepts of being on top of other apps and allow dragging a handle to show the content on its left side (like a navigation drawer)
Maybe this could show what I mean:
As far as I know, putting anything on top using a system-alert permission requires knowing the size of the view.
However, this is different, since i can't set it to be the entire screen because i don't want to block the rest of the screen in case the user sees only the handle of the navigation drawer.
The question
Is it possible to merge the 2 concepts ?
How would I allow all states to behave nicely while being on top?
for avoiding blocking of touches , I would also like to allow the user to drag the handle up and down, or maybe customize its position in some way.
回答1:
Based on a few ideas from https://github.com/NikolaDespotoski/DrawerLayoutEdgeToggle I have implemented a much simpler version of a handle for the NavigationDrawer.
Use like this:
View drawer = findViewById(R.id.drawer);
float verticalOffset = 0.2f;
DrawerHandle.attach(drawer, R.layout.handle, verticalOffset);
DrawerHandle:
import android.content.Context;
import android.graphics.Point;
import android.os.Build;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.ViewDragHelper;
import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
public class DrawerHandle implements DrawerLayout.DrawerListener {
public static final String TAG = "DrawerHandle";
private ViewGroup mRootView;
private DrawerLayout mDrawerLayout;
private View mHandle;
private View mDrawer;
private float mVerticalOffset;
private int mGravity;
private WindowManager mWM;
private Display mDisplay;
private Point mScreenDimensions = new Point();
private OnClickListener mHandleClickListener = new OnClickListener(){
@Override
public void onClick(View v) {
if(!mDrawerLayout.isDrawerOpen(mGravity)) mDrawerLayout.openDrawer(mGravity);
else mDrawerLayout.closeDrawer(mGravity);
}
};
private OnTouchListener mHandleTouchListener = new OnTouchListener() {
private static final int MAX_CLICK_DURATION = 200;
private long startClickTime;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startClickTime = System.currentTimeMillis();
break;
}
case MotionEvent.ACTION_UP: {
if(System.currentTimeMillis() - startClickTime < MAX_CLICK_DURATION) {
v.performClick();
return true;
}
}
}
MotionEvent copy = MotionEvent.obtain(event);
copy.setEdgeFlags(ViewDragHelper.EDGE_ALL);
copy.setLocation(event.getRawX() + (mGravity == Gravity.LEFT || mGravity == GravityCompat.START ? -mHandle.getWidth()/2 : mHandle.getWidth() / 2), event.getRawY());
mDrawerLayout.onTouchEvent(copy);
copy.recycle();
return true;
}
};
private int getDrawerViewGravity(View drawerView) {
final int gravity = ((DrawerLayout.LayoutParams) drawerView.getLayoutParams()).gravity;
return GravityCompat.getAbsoluteGravity(gravity, ViewCompat.getLayoutDirection(drawerView));
}
private float getTranslation(float slideOffset){
return (mGravity == GravityCompat.START || mGravity == Gravity.LEFT) ? slideOffset*mDrawer.getWidth() : -slideOffset*mDrawer.getWidth();
}
private void updateScreenDimensions() {
if (Build.VERSION.SDK_INT >= 13) {
mDisplay.getSize(mScreenDimensions);
} else {
mScreenDimensions.x = mDisplay.getWidth();
mScreenDimensions.y = mDisplay.getHeight();
}
}
private DrawerHandle(DrawerLayout drawerLayout, View drawer, int handleLayout, float handleVerticalOffset) {
mDrawer = drawer;
mGravity = getDrawerViewGravity(mDrawer);
mDrawerLayout = drawerLayout;
mRootView = (ViewGroup)mDrawerLayout.getRootView();
LayoutInflater inflater = (LayoutInflater) mDrawerLayout.getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE );
mHandle = inflater.inflate(handleLayout, mRootView, false);
mWM = (WindowManager) mDrawerLayout.getContext().getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWM.getDefaultDisplay();
mHandle.setOnClickListener(mHandleClickListener);
mHandle.setOnTouchListener(mHandleTouchListener);
mRootView.addView(mHandle, new FrameLayout.LayoutParams(mHandle.getLayoutParams().width, mHandle.getLayoutParams().height, mGravity));
setVerticalOffset(handleVerticalOffset);
mDrawerLayout.setDrawerListener(this);
}
public static DrawerHandle attach(View drawer, int handleLayout, float verticalOffset) {
if (!(drawer.getParent() instanceof DrawerLayout)) throw new IllegalArgumentException("Argument drawer must be direct child of a DrawerLayout");
return new DrawerHandle((DrawerLayout)drawer.getParent(), drawer, handleLayout, verticalOffset);
}
public static DrawerHandle attach(View drawer, int handleLayout) {
return attach(drawer, handleLayout, 0);
}
@Override
public void onDrawerClosed(View arg0) {
}
@Override
public void onDrawerOpened(View arg0) {
}
@Override
public void onDrawerSlide(View arg0, float slideOffset) {
float translationX = getTranslation(slideOffset);
mHandle.setTranslationX(translationX);
}
@Override
public void onDrawerStateChanged(int arg0) {
}
public View getView(){
return mHandle;
}
public View getDrawer() {
return mDrawer;
}
public void setVerticalOffset(float offset) {
updateScreenDimensions();
mVerticalOffset = offset;
mHandle.setY(mVerticalOffset*mScreenDimensions.y);
}
}
Layout:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.fscz.views.BounceViewPager
android:id="@+id/content_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
/>
<com.fscz.views.CirclePageIndicator
android:id="@+id/content_indicator"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="10dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:layout_marginTop="100dp"
style="@style/link"
/>
</RelativeLayout>
<LinearLayout
android:id="@+id/drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="right"
android:orientation="vertical"
android:padding="20dp"
android:background="@color/black_transparent"
>
<TextView
android:layout_width="240dp"
android:layout_height="wrap_content"
style="@style/text"
android:text="@string/collections"
android:paddingBottom="20dp"
/>
<ListView
android:id="@+id/drawer_list"
android:layout_width="240dp"
android:layout_height="0dip"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:layout_weight="1"
/>
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
Activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_browse);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawer = findViewById(R.id.drawer);
mDrawerList = (ListView) findViewById(R.id.drawer_list);
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, Preferences.getKnownCollections()));
mDrawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapter, View view, int pos, long id) {
Preferences.setActiveCollection(Preferences.getKnownCollections()[pos]);
loader.loadAll(Preferences.getKnownCollections()[pos], BrowseActivity.this);
mDrawerLayout.closeDrawers();
}
});
DrawerHandle.attach(mDrawer, R.layout.handle, 0.2f);
}
回答2:
Check this library out! Very simple implementation.
https://github.com/kedzie/DraggableDrawers
回答3:
Its quite an interesting idea you got there. From what I've understood so far; you want to create a navigation drawer type view and implement it like a floating overlay.
It is possible. I have found possible resources which may help you. You could either use a Window manager,wrap the navigation drawer view within the Window Manager, call it with a service. And Track the user touch motion with the WinodowManager. LaoutParams.(with the x & y position of the touch; using onTouchListner)
Here is an open source project where they are trying to create a facebook chat head like feature using WindowManager
There is also an Android library, which you can creating floating windows.I presume the app you pointed out earlier AirCalc etc are using a customized version of this project.
Here is a simple Demo of this project. and here is the link to the project StandOut lib project
Regards -Sathya
回答4:
This can work from API level 8 because there is no View.setX
before API level 11 I guess.
You can put this inside your OnCreate
:
YOU_DRAWER.setDrawerListener(this);
and override this method and implement this DrawerListener
on your activity:
@Override
public void onDrawerSlide(View arg0, float arg1) {
/// then arg0 is your drawer View
/// the arg1 is your offset of the drawer in the screen
params.leftMargin = (int) (arg0.getWidth()*arg1);
YOUR_VIEW.setLayoutParams(params);
}
来源:https://stackoverflow.com/questions/18768360/draggable-drawer-with-a-handle-instead-of-action-bar-on-top-of-other-apps