开源项目:AndroidSideMenu(轻量级侧边栏)

Android 侧边菜单栏实现全攻略 本文还有配套的精品资源,点击获取 简介:在Android开发中,侧边菜单是常用的导航设计模式。文章将详细介绍侧边菜单的设计原理和实现方法,包括使用SlidingMenu库和NavigationView组件的步骤、注意事项和最佳实践。读者将通过这些内容,学习如何创建一个响应迅速、用户友好的侧边菜单,以增强应用的导航体验。 1. Android 侧边菜单概念及重要性 ... 阅读详情

项目地址:https://github.com/dmitry-zaitsev/AndroidSideMenu

这个侧边栏的代码只有一个文件:SlideHolder.java

源码分析如下所示:

/*
 * Copyright dmitry.zaicew@gmail.com Dmitry Zaitsev
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package com.agimind.widget;

import java.util.LinkedList;
import java.util.Queue;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.Region.Op;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
import android.widget.FrameLayout;

/**
 * 轻量级侧边栏:分为菜单栏和内容栏。
 * 仅支持从左边或右边开启侧边栏菜单,不支持同时,且滑开菜单栏的动画不能修改;
 * 实现方法:滑动的是内容栏,菜单栏不动。这个是使用Canvas.translate()配合动画来完成内容栏滑动效果的。
 * 不过我们自己写的话,大多数会采用scrollTo()函数来完成。
 */
public class SlideHolder extends FrameLayout {
	public final static int DIRECTION_LEFT = 1; // 左侧边栏
	public final static int DIRECTION_RIGHT = -1; // 右侧边栏
	
	protected final static int MODE_READY = 0; // 标记菜单栏还没有打开,可以滑动侧边栏
	protected final static int MODE_SLIDE = 1; // 标记菜单栏正在划开侧边栏过程中...
	protected final static int MODE_FINISHED = 2; // 标记菜单栏是否已经打开
	
	private Bitmap mCachedBitmap; // 与内容栏的宽高相等的图片
	private Canvas mCachedCanvas; // 画布,用于
	private Paint mCachedPaint;
	
	private View mMenuView; // 菜单栏

	private int mMode = MODE_READY;
	private int mDirection = DIRECTION_LEFT; // 从左边打开侧边栏或右边

	private int mOffset = 0; // 内容栏的当前偏移量
	private int mStartOffset; // 内容栏开始移动的位置的偏移量
	private int mEndOffset; // 内容栏移动结束的位置的偏移量

	private boolean mEnabled = true;
	private boolean mInterceptTouch = true; // 标记是否允许侧边栏截获触摸手势
	private boolean mAlwaysOpened = false; // 标记侧边栏是否是持续打开的,用于大屏幕的Pad
	private boolean mDispatchWhenOpened = false; // 标记当侧边栏打开时,是否分发触摸手势

	private Queue<Runnable> mWhenReady = new LinkedList<Runnable>(); // 打开/关闭菜单栏的线程对象集合
	private OnSlideListener mListener; // 滑动菜单栏结束的回调

	public SlideHolder(Context context) {
		super(context);
		initView();
	}

	public SlideHolder(Context context, AttributeSet attrs) {
		super(context, attrs);
		initView();
	}

	public SlideHolder(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		initView();
	}

	private void initView() {
		mCachedPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG 
				| Paint.DITHER_FLAG);
	}

	@Override
	public void setEnabled(boolean enabled) {
		mEnabled = enabled;
	}

	@Override
	public boolean isEnabled() {
		return mEnabled;
	}

	/**
	 * 设置侧边栏打开方向
	 * 
	 * @param direction
	 *            - direction in which SlideHolder opens. Can be:
	 *            DIRECTION_LEFT, DIRECTION_RIGHT
	 */
	public void setDirection(int direction) {
		closeImmediately();
		mDirection = direction;
	}

	/**
	 * 设置允许截获触摸手势事件
	 * 
	 * @param allow
	 *            - if false, SlideHolder won't react to swiping gestures (but
	 *            still will be able to work by manually invoking mathods)
	 */
	public void setAllowInterceptTouch(boolean allow) {
		mInterceptTouch = allow;
	}

	/**
	 * 判断是否允许侧边栏截获触摸手势事件
	 * @return
	 */
	public boolean isAllowedInterceptTouch() {
		return mInterceptTouch;
	}

	/**
	 * 判断是否允许侧边栏分发触摸手势事件
	 * @param dispatch
	 *            - if true, in open state SlideHolder will dispatch touch
	 *            events to main layout (in other words - it will be clickable)
	 */
	public void setDispatchTouchWhenOpened(boolean dispatch) {
		mDispatchWhenOpened = dispatch;
	}

	public boolean isDispatchTouchWhenOpened() {
		return mDispatchWhenOpened;
	}

	/**
	 * 设置侧边栏总是打开,用于大屏幕的Pad设备
	 * @param opened
	 *            - if true, SlideHolder will always be in opened state (which
	 *            means that swiping won't work)
	 */
	public void setAlwaysOpened(boolean opened) {
		mAlwaysOpened = opened;
		requestLayout();
	}

	/**
	 * 获取侧边栏菜单的偏移量
	 * @return
	 */
	public int getMenuOffset() {
		return mOffset;
	}

	public void setOnSlideListener(OnSlideListener lis) {
		mListener = lis;
	}

	/**
	 * 菜单栏是否已经打开
	 * @return
	 */
	public boolean isOpened() {
		return mAlwaysOpened || mMode == MODE_FINISHED;
	}

	/**
	 * 菜单栏的开关
	 * @param immediately
	 */
	public void toggle(boolean immediately) {
		if (immediately) {
			toggleImmediately();
		} else {
			toggle();
		}
	}

	/**
	 * 菜单栏开关
	 */
	public void toggle() {
		if (isOpened()) {
			close();
		} else {
			open();
		}
	}

	/**
	 *  菜单栏立即开关
	 */
	public void toggleImmediately() {
		if (isOpened()) {
			closeImmediately();
		} else {
			openImmediately();
		}
	}

	/**
	 * 开启菜单栏
	 * @return
	 */
	public boolean open() {
		if (isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
			return false;
		}

		if (!isReadyForSlide()) {
			mWhenReady.add(new Runnable() {
				@Override
				public void run() {
					open();
				}
			});

			return true;
		}

		initSlideMode();

		Animation anim = new SlideAnimation(mOffset, mEndOffset);
		anim.setAnimationListener(mOpenListener);
		startAnimation(anim);

		invalidate();

		return true;
	}

	/**
	 * 立即打开侧边栏菜单
	 * @return
	 */
	public boolean openImmediately() {
		if (isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
			return false;
		}

		if (!isReadyForSlide()) {
			mWhenReady.add(new Runnable() {
				@Override
				public void run() {
					openImmediately();
				}
			});

			return true;
		}

		mMenuView.setVisibility(View.VISIBLE);
		mMode = MODE_FINISHED;
		requestLayout();

		if (mListener != null) {
			mListener.onSlideCompleted(true);
		}

		return true;
	}

	/**
	 * 关闭菜单栏
	 * @return
	 */
	public boolean close() {
		if (!isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
			return false;
		}

		if (!isReadyForSlide()) {
			mWhenReady.add(new Runnable() {
				@Override
				public void run() {
					close();
				}
			});
			return true;
		}

		initSlideMode();

		Animation anim = new SlideAnimation(mOffset, mEndOffset); // 关闭菜单栏的动画
		anim.setAnimationListener(mCloseListener);
		startAnimation(anim);
		
		invalidate();
		return true;
	}

	/**
	 * 快速关闭菜单栏
	 * @return
	 */
	public boolean closeImmediately() {
		if (!isOpened() || mAlwaysOpened || mMode == MODE_SLIDE) {
			return false;
		}

		if (!isReadyForSlide()) { // 侧边栏还没有准备好滑动,则加入队列中等待执行关闭菜单栏
			mWhenReady.add(new Runnable() {
				@Override
				public void run() {
					closeImmediately();
				}
			});

			return true;
		}

		mMenuView.setVisibility(View.GONE); // 直接把菜单栏设为GONE(够快速了吧!!)
		mMode = MODE_READY;
		requestLayout();

		if (mListener != null) {
			mListener.onSlideCompleted(false);
		}

		return true;
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		final int parentLeft = 0;
		final int parentTop = 0;
		final int parentRight = r - l;
		final int parentBottom = b - t;

		View menu = getChildAt(0);
		int menuWidth = menu.getMeasuredWidth();

		if (mDirection == DIRECTION_LEFT) {
			menu.layout(parentLeft, parentTop, parentLeft + menuWidth, parentBottom);
		} else {
			menu.layout(parentRight - menuWidth, parentTop, parentRight, parentBottom);
		}

		if (mAlwaysOpened) {
			if (mDirection == DIRECTION_LEFT) {
				mOffset = menuWidth;
			} else {
				mOffset = 0;
			}
		} else if (mMode == MODE_FINISHED) {
			mOffset = mDirection * menuWidth;
		} else if (mMode == MODE_READY) {
			mOffset = 0;
		}

		View main = getChildAt(1);
		main.layout(parentLeft + mOffset, parentTop, parentLeft + mOffset
				+ main.getMeasuredWidth(), parentBottom);

		invalidate();

		Runnable rn;
		while ((rn = mWhenReady.poll()) != null) {
			rn.run();
		}
	}

	/**
	 * 是否准备好打开菜单栏或关闭菜单栏,即是否可以滑动??
	 * @return 返回false的唯一情况是:当前正在滑动菜单栏中...
	 */
	private boolean isReadyForSlide() {
		return (getWidth() > 0 && getHeight() > 0);
	}
	
	@Override
	protected void onMeasure(int wSp, int hSp) {
		mMenuView = getChildAt(0);

		if (mAlwaysOpened) {
			View main = getChildAt(1);

			if (mMenuView != null && main != null) {
				measureChild(mMenuView, wSp, hSp);
				LayoutParams lp = (LayoutParams) main.getLayoutParams();

				if (mDirection == DIRECTION_LEFT) {
					lp.leftMargin = mMenuView.getMeasuredWidth();
				} else {
					lp.rightMargin = mMenuView.getMeasuredWidth();
				}
			}
		}

		super.onMeasure(wSp, hSp);
	}
	
	private byte mFrame = 0;

	@Override
	protected void dispatchDraw(Canvas canvas) {
		try {
			if (mMode == MODE_SLIDE) {
				View main = getChildAt(1);
				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
					
					/*
					 * On new versions we redrawing main layout only if it's
					 * marked as dirty
					 */
					if (main.isDirty()) {
						mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
						main.draw(mCachedCanvas);
					}
				} else {
					/*
					 * On older versions we just redrawing our cache every 5th
					 * frame
					 */
					if (++mFrame % 5 == 0) {
						mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
						main.draw(mCachedCanvas);
					}
				}

				/*
				 * Draw only visible part of menu
				 */

				View menu = getChildAt(0);
				final int scrollX = menu.getScrollX();
				final int scrollY = menu.getScrollY();

				canvas.save();

				if (mDirection == DIRECTION_LEFT) {
					canvas.clipRect(0, 0, mOffset, menu.getHeight(), Op.REPLACE);
				} else {
					int menuWidth = menu.getWidth();
					int menuLeft = menu.getLeft();

					canvas.clipRect(menuLeft + menuWidth + mOffset, 0, menuLeft 
							+ menuWidth, menu.getHeight());
				}

				canvas.translate(menu.getLeft(), menu.getTop());
				canvas.translate(-scrollX, -scrollY);
				menu.draw(canvas);
				canvas.restore();
				canvas.drawBitmap(mCachedBitmap, mOffset, 0, mCachedPaint);
			} else {
				if (!mAlwaysOpened && mMode == MODE_READY) {
					mMenuView.setVisibility(View.GONE);
				}

				super.dispatchDraw(canvas);
			}
		} catch (IndexOutOfBoundsException e) {
			/*
			 * Possibility of crashes on some devices (especially on Samsung).
			 * Usually, when ListView is empty.
			 */
		}
	}

	private int mHistoricalX = 0;
	private boolean mCloseOnRelease = false;

	/**
	 * 分发触摸手势事件,这个是触摸手势被识别进入的第一个函数
	 */
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		if (((!mEnabled || !mInterceptTouch) && mMode == MODE_READY) || mAlwaysOpened) {
			return super.dispatchTouchEvent(ev);
		}

		if (mMode != MODE_FINISHED) {
			onTouchEvent(ev);

			if (mMode != MODE_SLIDE) {
				super.dispatchTouchEvent(ev);
			} else {
				MotionEvent cancelEvent = MotionEvent.obtain(ev);
				cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
				super.dispatchTouchEvent(cancelEvent);
				cancelEvent.recycle();
			}

			return true;
		} else {
			final int action = ev.getAction();

			Rect rect = new Rect();
			View menu = getChildAt(0);
			menu.getHitRect(rect);

			if (!rect.contains((int) ev.getX(), (int) ev.getY())) {
				if (action == MotionEvent.ACTION_UP && mCloseOnRelease && !mDispatchWhenOpened) {
					close();
					mCloseOnRelease = false;
				} else {
					if (action == MotionEvent.ACTION_DOWN && !mDispatchWhenOpened) {
						mCloseOnRelease = true;
					}

					onTouchEvent(ev);
				}

				if (mDispatchWhenOpened) {
					super.dispatchTouchEvent(ev);
				}

				return true;
			} else {
				onTouchEvent(ev);
				ev.offsetLocation(-menu.getLeft(), -menu.getTop());
				menu.dispatchTouchEvent(ev);
				return true;
			}
		}
	}

	/**
	 * 处理触摸手势事件
	 * @param ev
	 * @return
	 */
	private boolean handleTouchEvent(MotionEvent ev) {
		if (!mEnabled) {
			return false;
		}

		float x = ev.getX();

		if (ev.getAction() == MotionEvent.ACTION_DOWN) {
			mHistoricalX = (int) x;
			return true;
		}

		if (ev.getAction() == MotionEvent.ACTION_MOVE) {
			float diff = x - mHistoricalX;

			// 判断是可以认为是滑动手势的
			if ((mDirection * diff > 50 && mMode == MODE_READY)
					|| (mDirection * diff < -50 && mMode == MODE_FINISHED)) {
				
				mHistoricalX = (int) x;
				initSlideMode();
			} else if (mMode == MODE_SLIDE) { // 正处于滑动过程中...
				mOffset += diff;
				mHistoricalX = (int) x;
				if (!isSlideAllowed()) {
					finishSlide();
				}
			} else {
				return false;
			}
		}

		if (ev.getAction() == MotionEvent.ACTION_UP) {
			if (mMode == MODE_SLIDE) {
				finishSlide();
			}

			mCloseOnRelease = false;
			return false;
		}

		return mMode == MODE_SLIDE;
	}

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		boolean handled = handleTouchEvent(ev);
		invalidate();
		return handled;
	}

	/*
	 * 初始化侧边栏菜单的模式:打开和关闭菜单栏的时候都需要重新设置 侧边栏参数,
	 * 这个函数在配合onLayout()函数,就实现了侧边栏动画。
	 */
	private void initSlideMode() {
		mCloseOnRelease = false;

		View v = getChildAt(1); // 获取内容栏

		if (mMode == MODE_READY) { // 侧边栏菜单未打开时
			mStartOffset = 0;
			mEndOffset = mDirection * getChildAt(0).getWidth();
		} else { // 侧边栏菜单界面已经打开后
			mStartOffset = mDirection * getChildAt(0).getWidth();
			mEndOffset = 0; // 内容界面最后位置的偏移量
		}

		mOffset = mStartOffset; // 设置当前内容栏的偏移量
		
		if (mCachedBitmap == null || mCachedBitmap.isRecycled() 
				|| mCachedBitmap.getWidth() != v.getWidth()) {
			
			mCachedBitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), 
					Bitmap.Config.ARGB_8888);
			
			mCachedCanvas = new Canvas(mCachedBitmap);
		} else {
			mCachedCanvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
		}

		v.setVisibility(View.VISIBLE);

		mCachedCanvas.translate(-v.getScrollX(), -v.getScrollY());
		v.draw(mCachedCanvas);

		mMode = MODE_SLIDE;
		mMenuView.setVisibility(View.VISIBLE);
	}

	/*
	 * 是否允许滑动
	 */
	private boolean isSlideAllowed() {
		return (mDirection * mEndOffset > 0 && mDirection * mOffset < mDirection * mEndOffset 
					&& mDirection * mOffset >= mDirection * mStartOffset) 
				|| (mEndOffset == 0 && mDirection * mOffset > mDirection * mEndOffset 
					&& mDirection * mOffset <= mDirection * mStartOffset);
	}

	/*
	 * 打开完毕的回调
	 */
	private void completeOpening() {
		mOffset = mDirection * mMenuView.getWidth(); // 设置当前内容栏的偏移量
		requestLayout();

		post(new Runnable() {
			@Override
			public void run() {
				mMode = MODE_FINISHED;
				mMenuView.setVisibility(View.VISIBLE);
			}
		});

		if (mListener != null) {
			mListener.onSlideCompleted(true);
		}
	}

	/**
	 * 以动画方式打开菜单栏的回调函数
	 */
	private Animation.AnimationListener mOpenListener = new Animation.AnimationListener() {
		@Override
		public void onAnimationStart(Animation animation) {
		}

		@Override
		public void onAnimationRepeat(Animation animation) {
		}

		@Override
		public void onAnimationEnd(Animation animation) {
			completeOpening();
		}
	};

	/**
	 * 完成关闭菜单栏的回调
	 */
	private void completeClosing() {
		mOffset = 0;
		requestLayout();

		post(new Runnable() {
			@Override
			public void run() {
				mMode = MODE_READY;
				mMenuView.setVisibility(View.GONE);
			}
		});

		if (mListener != null) {
			mListener.onSlideCompleted(false);
		}
	}

	/**
	 * 关闭菜单栏的动画回调
	 */
	private Animation.AnimationListener mCloseListener = new Animation.AnimationListener() {
		@Override
		public void onAnimationStart(Animation animation) {
		}

		@Override
		public void onAnimationRepeat(Animation animation) {
		}

		@Override
		public void onAnimationEnd(Animation animation) {
			completeClosing();
		}
	};

	/*
	 * 结束滑动
	 */
	private void finishSlide() {
		if (mDirection * mEndOffset > 0) { // 菜单栏在左侧
			
			// 如果当前滑动距离大于结束距离的一般,则认为是滑动操作
			if (mDirection * mOffset > mDirection * mEndOffset / 2) {
				if (mDirection * mOffset > mDirection * mEndOffset) { // 防止滑过界
					mOffset = mEndOffset;
				}
				
				// 打开菜单栏动画
				Animation anim = new SlideAnimation(mOffset, mEndOffset);
				anim.setAnimationListener(mOpenListener);
				startAnimation(anim);
			} else {
				if (mDirection * mOffset < mDirection * mStartOffset) { // 防止滑过界
					mOffset = mStartOffset;
				}

				// 关闭菜单栏动画
				Animation anim = new SlideAnimation(mOffset, mStartOffset);
				anim.setAnimationListener(mCloseListener);
				startAnimation(anim);
			}
		} else { // 菜单栏在右侧
			if (mDirection * mOffset < mDirection * mStartOffset / 2) {
				if (mDirection * mOffset < mDirection * mEndOffset) {
					mOffset = mEndOffset;
				}

				Animation anim = new SlideAnimation(mOffset, mEndOffset);
				anim.setAnimationListener(mCloseListener);
				startAnimation(anim);
			} else {
				if (mDirection * mOffset > mDirection * mStartOffset) {
					mOffset = mStartOffset;
				}

				Animation anim = new SlideAnimation(mOffset, mStartOffset);
				anim.setAnimationListener(mOpenListener);
				startAnimation(anim);
			}
		}
	}

	/*
	 * 偏移动画
	 */
	private class SlideAnimation extends Animation {
		private static final float SPEED = 0.6f;

		private float mStart;
		private float mEnd;

		public SlideAnimation(float fromX, float toX) {
			mStart = fromX;
			mEnd = toX;

			setInterpolator(new DecelerateInterpolator());

			float duration = Math.abs(mEnd - mStart) / SPEED;
			setDuration((long) duration);
		}

		@Override
		protected void applyTransformation(float interpolatedTime, Transformation t) {
			super.applyTransformation(interpolatedTime, t);

			float offset = (mEnd - mStart) * interpolatedTime + mStart;
			mOffset = (int) offset;

			postInvalidate();
		}
	}

	public static interface OnSlideListener {
		public void onSlideCompleted(boolean opened);
	}
}

Android代码-做的很不错的一款侧边栏索引。 WaveSideBar project 一个快速跳跃分组的侧边栏控件,示例中配合RecyclerView实现。 波浪的实现采用了三段二次贝塞尔曲线,附一张手稿草图,仅供参考。 Usage use WaveSideBarView in the layout file set OnTouchLetterChangeListener mSideBarView.setOnTouchLetterChangeListener(new WaveSideBarView.OnTouchLetterChangeListener() { @Override public void onLetterChange(String letter) { int pos = adapter.getLetterPosition(letter); if (pos != -1) { mRecyclerView.scrollToPosition(pos); 立即下载

相关推荐

可自定义侧边栏特效源码.zip

可自定义侧边栏特效源码,在onIndexSideBarTouch触摸事件回调接口的MotionEvent.ACTION_DOWN和MotionEvent.ACTION_UP的时候做改变侧边栏的背景颜色操作和索引显示TextView可见性设置;在onIndexChange索引改变事件回调接口中做索引显示TextView改变索引值的动作。  

android 侧边栏菜单,开源强大侧栏菜单

源码片段package net.simonvt.menudrawer.samples;import net.simonvt.menudrawer.MenuDrawer;import net.simonvt.menudrawer.Position;import android.os.Build;import android.os.Bundle;import android.support.v4.ap...

weixin_33268725的博客 818

SideMenu:SideMenu是用于SwiftUI的简单Android导航抽屉实现

侧边菜单 概述 SideMenu是用SwiftUI为SwiftUI编写的一个简单的侧边菜单导航控件 预览样本 左面板 右面板 要求 Xcode 11 斯威夫特5.1 iOS 13或更高版本。 安装 迅捷软件包管理器 在导航器中选择项目,转到“ Swift软件包”选项卡并添加以下URL https://github.com/Vidhyadharan-Mohanram/SideMenu 用法 初始化 SideMenu为中心视图采用单一视图绑定,并作为参数将SideMenuConfig类的实例作为参数,取决于需求SideMenu可以使用左侧,右侧或同时使用两个面板进行初始化。 import SideMenu struct MainView : View { var body: some View { SideMenu ( leftMenu : LeftMen

自定义组件开发九 侧边栏

概述 侧边栏是一种常见的 UI 结构,用户使用手指左滑或者右滑,可以像抽屉一样拉出隐藏在屏 幕边界之外的内容,既能增加 App 的 UI 的内容,又能给用户带来更新鲜的用户体验,网易新闻、QQ(如图所示)等主流 App 都有类似的设计。 侧边栏和上一章节讲的触摸滑屏有很多的类似之处。在侧边栏自定义容器中,定义两个子容 器,一个用作侧边栏,另一个用作主界面,默认情况下隐藏侧边栏,用户向右...

编程之美 1415

Android开源AndroidSideMenu实现抽屉和侧滑菜单

主要为大家详细介绍了Android开源AndroidSideMenu实现抽屉和侧滑菜单,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

ZYNQ学习:PL端DDR3 读写测试

DDR SDRAM 的最大特点是双沿触发,即在时钟的上升沿和下降沿都能进行数据采集和发送,同样的工作时钟, DDR SDRAM 的读写速度可以比传统的 SDRAM 快一倍。本次实验使用的 DDR3 芯片是南亚的 NT5CC256M16ER-EKI, NT5CC256M16ER-EKI 的 bank 位宽为 3,行位宽为 15,列位宽为 10,所以它的地址大小等于 2^3*2^15*2^10(即 2^28=256M),数据位宽为 16bit,所以容量大小为 256M*16bit,也就是 512MByte。

shuidiQAQ的博客 3032

【前端WEB】2、使用HTML+CSS+JS实现网页侧边导航栏效果(原创含源码)

侧边导航栏是现代网页设计中常用的一种元素,它通过优化页面结构,使用户能够轻松访问网站的不同部分。我们将通过具体的例子来展示如何创建一个具有良好用户界面和交互体验的侧边导航栏。以上就是今天要讲的内容,本文仅仅简单介绍了HTML前端页面的侧边栏的使用,而CSS提供了具体的样式,可以自己进行调节,JS实现了当用户点击侧边按钮即跳转。如需其他示例或有问题欢迎评论!!本人看到都会回复。

weixin_52938153的博客 9866

低成本实现webhook接收端[python]

Webook本质上也是API,只不过是反向调用,即前端不主动发送请求,完全由后端推送。简单来说,Webhook是一个接受HTTP POST或是GET,PUT,DELETE的URL,一个实现了Webhook的API提供商就是在当事件发生的时候会向配置的URL发送信息,与请求-响应式不同,使用Webhook可以实时接收到变化。Ps:Webhook可能会发出大量的请求,确保接受的URL不被泄露或者应用能及时处理这些请求。

锡城小凯的博客 1万+

vue3后台管理系统左右布局(侧边栏完整代码,直接复制即可)

vue3后台管理系统左右布局(侧边栏完整代码,直接复制即可)

m0_68330110的博客 1855

信捷XDH PLC多轴运动控制实战:Ethercat总线下的C语言封装技巧

本文深入探讨了在信捷XDH PLC平台上,如何利用C语言对EtherCAT总线多轴运动控制进行高效封装。通过设计全局数据池、模块化状态机及用户友好接口,将底层复杂的运动控制函数库抽象为清晰、可复用的高级模块,显著提升程序稳定性与可维护性,并分享了关键的实战避坑经验。

weixin_29188043的博客 160

本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印

本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印

ProceduralMan的博客 7410

每日一个CSS——导航栏

效果图 HTML代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>navigation</title> <link href="../css/nav.css" type="text/css" rel="stylesheet"> </head> <body> <div

winner_20020626的博客 591

Axure rp9 中继器新建表格

Axure rp9 中继器新建表格

weixin_49295490的博客 1652

android 侧边菜单,开源项目:AndroidSideMenu(轻量级侧边栏)

/** Licensed under the Apache License,Version 2.0 (the "License");* you may not use this file except incompliance with the License.* You may obtain a copy of the Licenseat**http://www.apache.org/licen...

weixin_42512719的博客 1028

Midscene.js(2):代码集成 + 桥接模式(Bridge Mode)的使用「喂饭教程」

本文将手把手教你从零开始搭建一个 Node.js 项目,集成 Midscene.js,并利用其桥接模式实现对 Chrome 浏览器的自动化操作。我们将通过具体的实例,来展示 Midscene.js 的便捷性与 AI 能力的结合。

blues_C的博客 1844

Unity 热更 之 【YooAsset 热更】几分钟快速了解 YooAsset [功能面板]、以及 [基础代码] 说明

Unity 的资源管理系统不断发展,以适应不同项目的需求和解决特定的问题。AssetBundle 和 Addressable Asset System 是 Unity 官方提供的资源管理解决方案,而 YooAsset 是第三方提供的资源管理系统。简化的API和易用性YooAsset 可能提供了更简单直观的 API,使得资源加载和管理更加容易。对于不熟悉 Addressable 或 AssetBundle 复杂配置的开发者来说,YooAsset 可能是一个更易于上手的选择。性能优化。

仙魁XAN 2956

HTML页面侧栏动态效果

代码如下所示: 页面侧栏动态效果 *{ padding:0px; margin:0px; } body{ background-color:skyblue; height:1200px; } #sidebar{ position:fixed;

MOONCOM的博客 4911

【LangChain编程:从入门到实践】实现多模态代理

【LangChain编程:从入门到实践】实现多模态代理 关键词:LangChain, 多模态代理, 自然语言处理, 机器学习, 知识图谱, 对话系统, 代码生成, AI代理 1. 背景介绍 随着人工智

欢迎来到我的优快云空间!这里聚焦AI大模型应用实战,分享前沿技术、实战案例与开发经验。 581

比心app源码,vue后台管理侧边栏菜单布局

比心app源码,vue后台管理侧边栏菜单布局 1.使用Element组件中NavMenu 导航菜单 <!-- 侧边栏 --> <el-aside width="212px"> <!-- 侧边栏菜单 --> <el-menu background-color="#333744" text-color="#fff" active-text-color="#ffd04b

云豹网络科技 495

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

【Android】侧边栏

在 Android 中实现侧边栏,通常可以使用来实现。是一个布局容器,可以包含两个子视图:一个是主内容区域,另一个是侧边栏。侧边栏可以从左侧或右侧滑出。

Mic666666的博客 724

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

android DrawerLayout 侧边栏实现

现在实现侧边栏比较简单了,官方提供的DrawerLayout可以很方便实现。 主要实现方法是:用DrawerLayout 作为界面根控件。在DrawerLayout里面第一个View为当前界面主内容;第二个和第三个View为抽屉菜单内容。如果当前界面只需要一个抽屉菜单,则第三个View可以省略。 第一个View的宽高应当设置为match_parent 第二、三个View需要设置android:

jifashihan的博客 7587

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

侧边栏代码 android,Android之DrawerLayout实现ViewPager+左右侧边栏

Android在V4包中提供了DrawerLayout布局来实现侧边栏的效果,代替了之前总是用第三方库实现的方法。关于DrawerLayout网上的介绍也很多,今天我们来实现一个基于DrawerLayout实现的Demo:框架结构:内容区域是ViewPager,里边包含Fragment,通过FragmentPageAdapter来与ViewPager关联。侧边栏我们用的是LinearLayout布...

weixin_32715971的博客 616

Android——SideMenu侧滑菜单的实现

最近有些忙,有一段时间没有写博客了,今天跟大家分享下SideMenu侧滑菜单的实现,自定义的Animation效果,给别人看起来就感觉好屌的样子,哈哈!不多说,先看看实现的效果。          总体来说也并不复杂,下面给大家看下整个项目的结构:       以下是实现的源码及详细步骤:   新建android studio项目我这边就不讲述了,说一下android studio关

Minor昔年 3032

侧边栏固定,响应式布局,附带原生实现和Vue版本源码

该布局适用于后台管理,主页展示等

qc15867301542的博客 2080

SourceTrail代码导航器现在免费开源

Sourcetrail是旨在简化源代码导航的工具,现已作为免费的开放源代码软件提供。 通过开源,Sourcetrail开发人员Coati Software放弃了其商业许可模式。 Sourcetrail定位为跨平台的源代码浏览器,可帮助开发人员提高对不熟悉的源代码的生产力。 目的是回答有关源代码的所有问题。 [ 同样在InfoWorld上:我们应该担心编程语言吗? ] 静态分析是在C...

cxu0262的博客 391

android开源项目(一) AndroidSideMenu

SlidingMenu实现的效果如下:            最近直接拿来用!很火的android开源项目真的很牛,作为菜鸟我也要学习大神们的经验,牛顿曾说过:“如果说我看的远,那是因为站在巨人的肩膀上” AndroidSideMenu的来源是github 代码库中的开源项目:路径如下:git://github.com/dmitry-zaitsev/AndroidSideMenu.g

程序员Android 2276
上一篇: 新建workspace的时常用Eclipse设置
下一篇: android编程小技巧集锦
HabbyGe
博客等级 码龄16年 46粉丝 143原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值