侧滑菜单

(1)自定义控件步骤:
     测量:onMeasure  设置自己显示在屏幕上的宽高
     布局:onLayout   设置自己显示在屏幕上的位置(只有在自定义ViewGroup中才用到)

    绘制:onDraw     控制显示在屏幕上的样子(自定义viewgroup时不需要这个) 

(2)ScrollView滚动视图是指当拥有很多内容,屏幕显示不完时,需要通过滚动跳来显示的视图。ScrollView只支持垂直滚动,且只能有一个子孩子,用布局包裹
(3)展示性图片没必要用一个ImageView来封装

<span style="font-size:14px;"><span style="font-size:14px;">    <TextView 
            style="@style/MenuTabText"
            android:background="#33aa9900"
            android:drawableLeft="@drawable/tab_news"
			android:drawpadding="15dp"
            android:text="新闻"/></span></span>

(4)抽取样式

<span style="font-size:14px;"><span style="font-size:14px;">    <style name="MenuTabText">
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:textColor">#ffffff</item>
        <item name="android:gravity">center_vertical</item>
        <item name="android:drawablePadding">15dp</item>
        <item name="android:padding">15dp</item>
        <item name="android:textSize">22sp</item>
        
    </style></span></span>

(4)单个Activity去掉标题栏

     requestWindowFeature(Window.FEATURE_NO_TITLE);

(5)   在ViewGroup中,让自己内容移动有以下几个方法:
layout(l,t,r,b);
offsetTopAndBottom(offset)和offsetLeftAndRight(offset);
scrollTo和scrollBy方法;
注意:滚动的并不是viewgroup内容本身,也不是屏幕的边框,而是viewgroup的矩形边框
 它是瞬间移动,

(6)在自定义ViewGroup中一般不需要去实现onMeasure,
 我们去实现系统已有的ViewGroup,比如FrameLayout,
 它会帮我们区实现onMeasure方法

不要继承ViewGroup,而去继承FrameLayout


(7)让view在一段时间内移动到某个位置
a.使用自定义动画(让view在一段时间内做某件事)
  
b.使用Scroller(模拟一个执行流程,)


(8)事件的分发机制











1.右边菜单布局

<span style="font-size:14px;"><?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="240dp"
    android:layout_height="match_parent"
    android:background="@drawable/menu_bg"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingBottom="300dp"
        android:orientation="vertical" >
        
        <TextView 
            style="@style/MenuTabText"
            android:background="#33aa9900"
            android:drawableLeft="@drawable/tab_news"
            android:text="新闻"/>
        <TextView 
            style="@style/MenuTabText"
            android:drawableLeft="@drawable/tab_read"
            android:text="订阅"/>
        <TextView 
            style="@style/MenuTabText"
            android:drawableLeft="@drawable/tab_ties"
            android:text="跟帖"/>
        <TextView 
            style="@style/MenuTabText"
            android:drawableLeft="@drawable/tab_pics"
            android:text="图片"/>
        <TextView 
            style="@style/MenuTabText"
            android:drawableLeft="@drawable/tab_ugc"
            android:text="话题"/>
        <TextView 
            style="@style/MenuTabText"
            android:drawableLeft="@drawable/tab_vote"
            android:text="投票"/>
        <TextView 
            style="@style/MenuTabText"
            android:drawableLeft="@drawable/tab_focus"
            android:text="聚合阅读"/>
        
    </LinearLayout>


</ScrollView></span>

2. 主布局

<span style="font-size:14px;"><?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="match_parent"
    android:background="#55666666"
    android:orientation="vertical" >
    
    <LinearLayout android:layout_width="match_parent"
        android:background="@drawable/top_bar_bg"
        android:gravity="center_vertical"
        android:layout_height="60dp">
        
        <ImageView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btn_back"
            android:background="@drawable/main_back"/>
        
        <View android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="5dp"
            android:background="@drawable/top_bar_divider"/>
        
        <TextView android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#ffffff"
            android:textSize="22sp"
            android:layout_marginLeft="15dp"
            android:text="黑马新闻"/>
        
    </LinearLayout>
    
    <TextView android:layout_width="match_parent"
        android:text="钓鱼岛是中国的..."
        android:textColor="#000000"
        android:textSize="30sp"
        android:gravity="center"
        android:layout_height="match_parent"/>

</LinearLayout></span>

3.总布局   SlideMenu extends FrameLayout   

<span style="font-size:14px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <com.heima52.slidemenu.view.SlideMenu
        android:id="@+id/slideMenu"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
        
        <!-- 菜单界面的布局 -->
        <include layout="@layout/layout_menu"/>
        
        
        <!-- 主界面的布局  -->
        <include layout="@layout/layout_main"/>
        
        
        
    </com.heima52.slidemenu.view.SlideMenu>

</RelativeLayout></span>

4. SlideMenu类

测量:onMeasure  设置自己显示在屏幕上的宽高
布局:onLayout   设置自己显示在屏幕上的位置(只有在自定义ViewGroup中才用到)
绘制:onDraw     控制显示在屏幕上的样子(自定义viewgroup时不需要这个)

*ViewGroup主要是控制子view如何摆放,所以必须实现onLayout

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.FrameLayout;
import android.widget.Scroller;

public class SlideMenu extends FrameLayout{
	private View menuView,mainView;
	private int menuWidth = 0;
	private Scroller scroller;
	public SlideMenu(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public SlideMenu(Context context) {
		super(context);
		init();
	}
	
	private void init(){
		scroller = new Scroller(getContext());
	}
	
	/**
	 * 当1级的子view全部加载完调用,可以用初始化子view的引用
	 * 注意,这里无法获取子view的宽高
	 */
	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		menuView = getChildAt(0);
		mainView = getChildAt(1);
		menuWidth = menuView.getLayoutParams().width;
	}
	
	/**
	 * widthMeasureSpec和heightMeasureSpec是系统测量SlideMenu时传入的参数,
	 * 这2个参数测量出的宽高能让SlideMenu充满窗体,其实是正好等于屏幕宽高
	 */
	 //设置自己显示在屏幕上的宽高
	 //传递的参数里有数据:
	 //widthMeasureSpec:SlideMenu在布局中使用的宽,即android:layout_width="match_parent"
	 //heightMeasureSpec:SlideMenu在布局中使用的高,即android:layout_height="match_parent"
//	@Override
//	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//		
//		int measureSpec = MeasureSpec.makeMeasureSpec(menuWidth, MeasureSpec.EXACTLY);
//		
//		//测量所有子view的宽高
//		//通过getLayoutParams方法可以获取到布局文件中指定宽高
//		menuView.measure(measureSpec, heightMeasureSpec);
//		//直接使用SlideMenu的测量参数,因为它的宽高都是充满父窗体
//		mainView.measure(widthMeasureSpec, heightMeasureSpec);
//		
//	}
	
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downX = (int) ev.getX();
			break;
		case MotionEvent.ACTION_MOVE:
			int deltaX = (int) ( ev.getX()- downX);
			
			if(Math.abs(deltaX)>8){
				return true;
			}
			break;
		}
		return super.onInterceptTouchEvent(ev);
//		return super.onInterceptTouchEvent(ev);
	}

	/**
	 * l: 当前子view的左边在父view的坐标系中的x坐标
	 * t: 当前子view的顶边在父view的坐标系中的y坐标
	 * r: 当前子view的右边在父view的坐标系中的x坐标
	 * t: 当前子view的底边在父view的坐标系中的y坐标
	 */
	 //ViewGroup中的布局放在哪里
	 //******坐标原点(0,0)在屏幕左上角
	 //onLayout中的参数是有数据的,即系统测量SlideMenu的数据,即:android:layout_width="match_parent"
	 //SlideMenu在父布局的宽高位置
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
//		Log.e("MAIN", "L: "+l+"   t: "+t  +"  r: "+r  + "   b: "+b);
		menuView.layout(-menuWidth, 0, 0, menuView.getMeasuredHeight());
		mainView.layout(0, 0, r, b);
	}
	
	private int downX;
	@Override
	public boolean onTouchEvent(MotionEvent event) {
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			downX = (int) event.getX();
			break;
		case MotionEvent.ACTION_MOVE:
			int moveX = (int) event.getX();
			int deltaX = (int) ( moveX- downX);
			
			int newScrollX = getScrollX() - deltaX;
			
			if(newScrollX<-menuWidth)newScrollX = -menuWidth;
			if(newScrollX>0)newScrollX = 0;
			
			Log.e("Main", "scrollX: "+getScrollX());
			scrollTo(newScrollX, 0);
			downX = moveX;
			break;
		case MotionEvent.ACTION_UP:
			//1.使用自定义动画
//			ScrollAnimation scrollAnimation;
//			if(getScrollX()>-menuWidth/2){
//				//关闭菜单
////				scrollTo(0, 0);
//				scrollAnimation = new ScrollAnimation(this, 0);
//			}else {
//				//打开菜单
////				scrollTo(-menuWidth, 0);
//				scrollAnimation = new ScrollAnimation(this, -menuWidth);
//			}
//			startAnimation(scrollAnimation);
			//2.使用Scroller
			if(getScrollX()>-menuWidth/2){
//				//关闭菜单
				closeMenu();
			}else {
				//打开菜单
				openMenu();
			}
			break;
		}
		return true;
	}
	
	private void closeMenu(){
		scroller.startScroll(getScrollX(), 0, 0-getScrollX(), 0, 400);
		invalidate();
	}
	
	private void openMenu(){
		scroller.startScroll(getScrollX(), 0, -menuWidth-getScrollX(), 0, 400);
		invalidate();
	}
	
	/**
	 * Scroller不主动去调用这个方法
	 * 而invalidate()可以掉这个方法
	 * invalidate->draw->computeScroll
	 */
	@Override
	public void computeScroll() {
		super.computeScroll();
		if(scroller.computeScrollOffset()){//返回true,表示动画没结束
			scrollTo(scroller.getCurrX(), 0);
			invalidate();
		}
	}

	/**
	 * 切换菜单的开和关
	 */
	public void switchMenu() {
		if(getScrollX()==0){
			//需要打开
			openMenu();
		}else {
			//需要关闭
			closeMenu();
		}
	}
	

}


5.动画类

import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * 让指定view在一段时间内scrollTo到指定位置
 * @author Administrator
 *
 */
public class ScrollAnimation extends Animation{
	
	private View view;
	private int targetScrollX;
	private int startScrollX;
	private int totalValue;
	
	public ScrollAnimation(View view, int targetScrollX) {
		super();
		this.view = view;
		this.targetScrollX = targetScrollX;
		
		startScrollX = view.getScrollX();
		totalValue = this.targetScrollX - startScrollX;
		
		int time = Math.abs(totalValue);
		setDuration(time);
	}



	/**
	 * 在指定的时间内一直执行该方法,直到动画结束
	 * interpolatedTime:0-1  标识动画执行的进度或者百分比
	 * time :  0   - 0.5  - 0.7  -   1
	 * value:  10  -  60  -  80  -  110
	 * 当前的值 = 起始值 + 总的差值*interpolatedTime
	 */
	@Override
	protected void applyTransformation(float interpolatedTime,
			Transformation t) {
		super.applyTransformation(interpolatedTime, t);
		int currentScrollX = (int) (startScrollX + totalValue*interpolatedTime);
		view.scrollTo(currentScrollX, 0);
	}
}


一、数据采集层:多源人脸数据获取 该层负责从不同设备 / 渠道采集人脸原始数据,为后续模型训练与识别提供基础样本,核心功能包括: 1. 多设备适配采集 实时摄像头采集: 调用计算机内置摄像头(或外接 USB 摄像头),通过OpenCV的VideoCapture接口实时捕获视频流,支持手动触发 “拍照”(按指定快捷键如Space)或自动定时采集(如每 2 秒采集 1 张),采集时自动框选人脸区域(通过Haar级联分类器初步定位),确保样本聚焦人脸。 支持采集参数配置:可设置采集分辨率(如 640×480、1280×720)、图像格式(JPG/PNG)、单用户采集数量(如默认采集 20 张,确保样本多样性),采集过程中实时显示 “已采集数量 / 目标数量”,避免样本不足。 本地图像 / 视频导入: 支持批量导入本地人脸图像文件(支持 JPG、PNG、BMP 格式),自动过滤非图像文件;导入视频文件(MP4、AVI 格式)时,可按 “固定帧间隔”(如每 10 帧提取 1 张图像)或 “手动选择帧” 提取人脸样本,适用于无实时摄像头场景。 数据集对接: 支持接入公开人脸数据集(如 LFW、ORL),通过预设脚本自动读取数据集目录结构(按 “用户 ID - 样本图像” 分类),快速构建训练样本库,无需手动采集,降低系统开发与测试成本。 2. 采集过程辅助功能 人脸有效性校验:采集时通过OpenCV的Haar级联分类器(或MTCNN轻量级模型)实时检测图像中是否包含人脸,若未检测到人脸(如遮挡、侧脸角度过大),则弹窗提示 “未识别到人脸,请调整姿态”,避免无效样本存入。 样本标签管理:采集时需为每个样本绑定 “用户标签”(如姓名、ID 号),支持手动输入标签或从 Excel 名单批量导入标签(按 “标签 - 采集数量” 对应),采集完成后自动按 “标签 - 序号” 命名文件(如 “张三
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值