├── .gitignore
├── AUTHOR
├── AndroidManifest.xml
├── LICENSE
├── README
├── build.xml
├── proguard.cfg
├── project.properties
├── res
├── drawable
│ └── icon.png
├── layout
│ └── main.xml
└── values
│ ├── ids.xml
│ └── strings.xml
└── src
└── org
└── zeroxlab
├── demo
└── MainActivity.java
└── widget
└── AnimationLayout.java
/.gitignore:
--------------------------------------------------------------------------------
1 | ant.properties
2 | bin/*
3 | gen/*
4 | local.properties
5 | proguard-project.txt
6 |
--------------------------------------------------------------------------------
/AUTHOR:
--------------------------------------------------------------------------------
1 | Julian Chu (a.k.a walkingice) walkingice0204 @t gmail dot com
2 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012 Julian Chu (a.k.a walkingice)
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | This project tries to provide a demo of widget AnimationLayout.
2 |
3 | The widget, AnimationLayout, works like Facebook Android app, it placed a sliding sidebar in left side.
4 |
5 |
6 | if you use command line to build android app
7 |
8 | $ git clone
9 | $ cd
10 | $ android update project -p .
11 | $ android debug install
12 |
13 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
29 |
30 |
31 |
40 |
41 |
42 |
43 |
47 |
48 |
49 |
51 |
63 |
64 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/proguard.cfg:
--------------------------------------------------------------------------------
1 | -optimizationpasses 5
2 | -dontusemixedcaseclassnames
3 | -dontskipnonpubliclibraryclasses
4 | -dontpreverify
5 | -verbose
6 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
7 |
8 | -keep public class * extends android.app.Activity
9 | -keep public class * extends android.app.Application
10 | -keep public class * extends android.app.Service
11 | -keep public class * extends android.content.BroadcastReceiver
12 | -keep public class * extends android.content.ContentProvider
13 | -keep public class * extends android.app.backup.BackupAgentHelper
14 | -keep public class * extends android.preference.Preference
15 | -keep public class com.android.vending.licensing.ILicensingService
16 |
17 | -keepclasseswithmembernames class * {
18 | native ;
19 | }
20 |
21 | -keepclasseswithmembers class * {
22 | public (android.content.Context, android.util.AttributeSet);
23 | }
24 |
25 | -keepclasseswithmembers class * {
26 | public (android.content.Context, android.util.AttributeSet, int);
27 | }
28 |
29 | -keepclassmembers class * extends android.app.Activity {
30 | public void *(android.view.View);
31 | }
32 |
33 | -keepclassmembers enum * {
34 | public static **[] values();
35 | public static ** valueOf(java.lang.String);
36 | }
37 |
38 | -keep class * implements android.os.Parcelable {
39 | public static final android.os.Parcelable$Creator *;
40 | }
41 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system use,
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 |
10 | # Project target.
11 | target=android-10
12 |
--------------------------------------------------------------------------------
/res/drawable/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/walkingice/gui-sliding-sidebar/44dde9e60d0a30b930677c4adbafb756663dc057/res/drawable/icon.png
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
13 |
17 |
22 |
28 |
29 |
34 |
35 |
36 |
37 |
45 |
51 |
57 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | DemoSidebar
4 |
5 |
--------------------------------------------------------------------------------
/src/org/zeroxlab/demo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package org.zeroxlab.demo;
2 |
3 | import org.zeroxlab.widget.AnimationLayout;
4 |
5 | import android.app.Activity;
6 | import android.app.ActivityManager;
7 | import android.os.Bundle;
8 | import android.widget.*;
9 | import android.util.Log;
10 | import android.view.View;
11 |
12 | public class MainActivity extends Activity implements AnimationLayout.Listener {
13 | public final static String TAG = "Demo";
14 |
15 | protected ListView mList;
16 | protected AnimationLayout mLayout;
17 | protected String[] mStrings = {"a", "b", "c", "d", "e", "f", "g", "h", "i"};
18 |
19 | @Override
20 | public void onCreate(Bundle savedInstanceState) {
21 | super.onCreate(savedInstanceState);
22 | setContentView(R.layout.main);
23 |
24 | mLayout = (AnimationLayout) findViewById(R.id.animation_layout);
25 | mLayout.setListener(this);
26 |
27 | mList = (ListView) findViewById(R.id.sidebar_list);
28 | mList.setAdapter(
29 | new ArrayAdapter(
30 | this, android.R.layout.simple_list_item_multiple_choice
31 | , mStrings));
32 | mList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
33 | }
34 |
35 | public void onClickContentButton(View v) {
36 | mLayout.toggleSidebar();
37 | }
38 |
39 | @Override
40 | public void onBackPressed() {
41 | if (mLayout.isOpening()) {
42 | mLayout.closeSidebar();
43 | } else {
44 | finish();
45 | }
46 | }
47 |
48 | /* Callback of AnimationLayout.Listener to monitor status of Sidebar */
49 | @Override
50 | public void onSidebarOpened() {
51 | Log.d(TAG, "opened");
52 | }
53 |
54 | /* Callback of AnimationLayout.Listener to monitor status of Sidebar */
55 | @Override
56 | public void onSidebarClosed() {
57 | Log.d(TAG, "opened");
58 | }
59 |
60 | /* Callback of AnimationLayout.Listener to monitor status of Sidebar */
61 | @Override
62 | public boolean onContentTouchedWhenOpening() {
63 | // the content area is touched when sidebar opening, close sidebar
64 | Log.d(TAG, "going to close sidebar");
65 | mLayout.closeSidebar();
66 | return true;
67 | }
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/src/org/zeroxlab/widget/AnimationLayout.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 0xlab - http://0xlab.org/
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * Authored by Julian Chu
17 | */
18 |
19 | package org.zeroxlab.widget;
20 |
21 | // update the package name to match your app
22 | import org.zeroxlab.demo.R;
23 |
24 | import android.content.Context;
25 | import android.util.AttributeSet;
26 | import android.view.animation.Animation;
27 | import android.view.animation.TranslateAnimation;
28 | import android.view.MotionEvent;
29 | import android.view.View;
30 | import android.view.View.MeasureSpec;
31 | import android.view.ViewGroup;
32 | import android.view.ViewGroup.LayoutParams;
33 |
34 | public class AnimationLayout extends ViewGroup {
35 |
36 | public final static int DURATION = 500;
37 |
38 | protected boolean mPlaceLeft = true;
39 | protected boolean mOpened;
40 | protected View mSidebar;
41 | protected View mContent;
42 | protected int mSidebarWidth = 150; /* assign default value. It will be overwrite
43 | in onMeasure by Layout xml resource. */
44 |
45 | protected Animation mAnimation;
46 | protected OpenListener mOpenListener;
47 | protected CloseListener mCloseListener;
48 | protected Listener mListener;
49 |
50 | protected boolean mPressed = false;
51 |
52 | public AnimationLayout(Context context) {
53 | this(context, null);
54 | }
55 |
56 | public AnimationLayout(Context context, AttributeSet attrs) {
57 | super(context, attrs);
58 | }
59 |
60 | @Override
61 | public void onFinishInflate() {
62 | super.onFinishInflate();
63 | mSidebar = findViewById(R.id.animation_layout_sidebar);
64 | mContent = findViewById(R.id.animation_layout_content);
65 |
66 | if (mSidebar == null) {
67 | throw new NullPointerException("no view id = animation_sidebar");
68 | }
69 |
70 | if (mContent == null) {
71 | throw new NullPointerException("no view id = animation_content");
72 | }
73 |
74 | mOpenListener = new OpenListener(mSidebar, mContent);
75 | mCloseListener = new CloseListener(mSidebar, mContent);
76 | }
77 |
78 | @Override
79 | public void onLayout(boolean changed, int l, int t, int r, int b) {
80 | /* the title bar assign top padding, drop it */
81 | int sidebarLeft = l;
82 | if (!mPlaceLeft) {
83 | sidebarLeft = r - mSidebarWidth;
84 | }
85 | mSidebar.layout(sidebarLeft,
86 | 0,
87 | sidebarLeft + mSidebarWidth,
88 | 0 + mSidebar.getMeasuredHeight());
89 |
90 | if (mOpened) {
91 | if (mPlaceLeft) {
92 | mContent.layout(l + mSidebarWidth, 0, r + mSidebarWidth, b);
93 | } else {
94 | mContent.layout(l - mSidebarWidth, 0, r - mSidebarWidth, b);
95 | }
96 | } else {
97 | mContent.layout(l, 0, r, b);
98 | }
99 | }
100 |
101 | @Override
102 | public void onMeasure(int w, int h) {
103 | super.onMeasure(w, h);
104 | super.measureChildren(w, h);
105 | mSidebarWidth = mSidebar.getMeasuredWidth();
106 | }
107 |
108 | @Override
109 | protected void measureChild(View child, int parentWSpec, int parentHSpec) {
110 | /* the max width of Sidebar is 90% of Parent */
111 | if (child == mSidebar) {
112 | int mode = MeasureSpec.getMode(parentWSpec);
113 | int width = (int)(getMeasuredWidth() * 0.9);
114 | super.measureChild(child, MeasureSpec.makeMeasureSpec(width, mode), parentHSpec);
115 | } else {
116 | super.measureChild(child, parentWSpec, parentHSpec);
117 | }
118 | }
119 |
120 | @Override
121 | public boolean onInterceptTouchEvent(MotionEvent ev) {
122 | if (!isOpening()) {
123 | return false;
124 | }
125 |
126 | int action = ev.getAction();
127 |
128 | if (action != MotionEvent.ACTION_UP
129 | && action != MotionEvent.ACTION_DOWN) {
130 | return false;
131 | }
132 |
133 | /* if user press and release both on Content while
134 | * sidebar is opening, call listener. otherwise, pass
135 | * the event to child. */
136 | int x = (int)ev.getX();
137 | int y = (int)ev.getY();
138 | if (mContent.getLeft() < x
139 | && mContent.getRight() > x
140 | && mContent.getTop() < y
141 | && mContent.getBottom() > y) {
142 | if (action == MotionEvent.ACTION_DOWN) {
143 | mPressed = true;
144 | }
145 |
146 | if (mPressed
147 | && action == MotionEvent.ACTION_UP
148 | && mListener != null) {
149 | mPressed = false;
150 | return mListener.onContentTouchedWhenOpening();
151 | }
152 | } else {
153 | mPressed = false;
154 | }
155 |
156 | return false;
157 | }
158 |
159 | public void setListener(Listener l) {
160 | mListener = l;
161 | }
162 |
163 | /* to see if the Sidebar is visible */
164 | public boolean isOpening() {
165 | return mOpened;
166 | }
167 |
168 | public void toggleSidebar() {
169 | if (mContent.getAnimation() != null) {
170 | return;
171 | }
172 |
173 | if (mOpened) {
174 | /* opened, make close animation*/
175 | if (mPlaceLeft) {
176 | mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
177 | } else {
178 | mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
179 | }
180 | mAnimation.setAnimationListener(mCloseListener);
181 | } else {
182 | /* not opened, make open animation */
183 | if (mPlaceLeft) {
184 | mAnimation = new TranslateAnimation(0, mSidebarWidth, 0, 0);
185 | } else {
186 | mAnimation = new TranslateAnimation(0, -mSidebarWidth, 0, 0);
187 | }
188 | mAnimation.setAnimationListener(mOpenListener);
189 | }
190 | mAnimation.setDuration(DURATION);
191 | mAnimation.setFillAfter(true);
192 | mAnimation.setFillEnabled(true);
193 | mContent.startAnimation(mAnimation);
194 | }
195 |
196 | public void openSidebar() {
197 | if (!mOpened) {
198 | toggleSidebar();
199 | }
200 | }
201 |
202 | public void closeSidebar() {
203 | if (mOpened) {
204 | toggleSidebar();
205 | }
206 | }
207 |
208 | class OpenListener implements Animation.AnimationListener {
209 | View iSidebar;
210 | View iContent;
211 |
212 | OpenListener(View sidebar, View content) {
213 | iSidebar = sidebar;
214 | iContent = content;
215 | }
216 |
217 | public void onAnimationRepeat(Animation animation) {
218 | }
219 |
220 | public void onAnimationStart(Animation animation) {
221 | iSidebar.setVisibility(View.VISIBLE);
222 | }
223 |
224 | public void onAnimationEnd(Animation animation) {
225 | iContent.clearAnimation();
226 | mOpened = !mOpened;
227 | requestLayout();
228 | if (mListener != null) {
229 | mListener.onSidebarOpened();
230 | }
231 | }
232 | }
233 |
234 | class CloseListener implements Animation.AnimationListener {
235 | View iSidebar;
236 | View iContent;
237 |
238 | CloseListener(View sidebar, View content) {
239 | iSidebar = sidebar;
240 | iContent = content;
241 | }
242 |
243 | public void onAnimationRepeat(Animation animation) {
244 | }
245 | public void onAnimationStart(Animation animation) {
246 | }
247 |
248 | public void onAnimationEnd(Animation animation) {
249 | iContent.clearAnimation();
250 | iSidebar.setVisibility(View.INVISIBLE);
251 | mOpened = !mOpened;
252 | requestLayout();
253 | if (mListener != null) {
254 | mListener.onSidebarClosed();
255 | }
256 | }
257 | }
258 |
259 | public interface Listener {
260 | public void onSidebarOpened();
261 | public void onSidebarClosed();
262 | public boolean onContentTouchedWhenOpening();
263 | }
264 | }
265 |
--------------------------------------------------------------------------------