精华内容
下载资源
问答
  • 手写记事本

    2014-05-06 17:40:31
    记事工具,便签,手写涂鸦,android
  • 自己写的小玩意。大家有兴趣的可以用用。仿window的记事本,还有不完整的地方。期待高手补充。本物件已封装。本机有了jdk双击文件就可以运行。
  • 大字标题模拟圆珠笔在记事本上面写字封面背景,记事本方格背景,简约扁平化设计,手写记事本背景简约时尚风总结报告通用ppt模板。
  • 笔记本记事本手写写字PPT模板。一套适合教育教学类幻灯片模板,手在记事本笔记本上写字背景。
  • 一个多功能的记事本支持手写画板插入图片即时拍照录音功能.rar,太多无法一一验证是否可用,程序如果跑不起来需要自调,部分代码功能进行参考学习。
  •  支持多种文档格式 ...甚至可单独应用于手写记事本手写记事本的背景图片、背景颜色可以随意定义。  高保真原始手写笔迹 笔迹信息经过抗锯齿运算,以矢量坐标保存。手写笔迹在显示时,可以无限放大而平滑无...

    在这里插入图片描述
     支持多种文档格式
    好签原笔迹手写技术可以在任何版式文档格式上进行手写与笔迹的展示,与文档类型无关,它是一款真正意义的跨文档格式的手写批示引擎,在常用的版式文件上都可以进行手写批示,如PDF、JGP、PNG、Html等。甚至可单独应用于手写记事本,手写记事本的背景图片、背景颜色可以随意定义。

     高保真原始手写笔迹
    笔迹信息经过抗锯齿运算,以矢量坐标保存。手写笔迹在显示时,可以无限放大而平滑无锯齿,真正保留了手写的原始笔迹。

     支持以笔画为单元擦除
    笔迹在进行擦除操作时,只要橡皮触碰到笔画的一部分,就会将整个笔画就会完整擦除。目前,市场上很多手写软件的擦除功能都是简单的按照像素进行擦除,想要完整的擦除一个字、一个笔画,需要反复涂画,非常费劲。如果两个字有重叠的部分,要完整擦除一个字,很容易误擦到旁边的字。

     支持存储签名和插入签名
    受限于手机屏幕尺寸,往往竖屏签字时,幅面过小,书写体验不佳,而插入签名功能则可以提供整个横屏作为手写板,用于签名,再将签名自动缩小放入文件中,这样的签名好签还支持以加密数据形式保存下来,供签署下一份文件时,直接调用。

     高速渲染
    具有较高的运算和显示效率:
    在这里插入图片描述
    如上图所示的大量和复杂笔画,好签绘制只需要1.7s的时间。实际应用中远没有如此复杂的笔画。

     性能稳定,安全可靠
    为防止手写批示数据被伪造,好签可以对笔迹数据利用签署者身份、物理环境特征等信息进行签名。采用多项加密技术结合电子签名,保证了批示笔迹不可篡改、批示人身份可识别、批示文件来源可验证,从而根本上解决了手写批示面临的安全问题。

    体验demo:
    http://www.dandanqian.com/

    展开全文
  • 想必大家都用过QQ的白板功能,里面主要有两项,一个是涂鸦功能,事实上类似于上节的画板功能,而还有一个就是手写,那记事本怎么能没有这个功能呢,今天就来为我们的记事本加入手写功能。 先上图,看看效果: ...

           想必大家都用过QQ的白板功能,里面主要有两项,一个是涂鸦功能,事实上类似于上节的画板功能,而还有一个就是手写,那记事本怎么能没有这个功能呢,今天就来为我们的记事本加入手写功能。

           先上图,看看效果:

           看了效果图,是不是心动了呢?那就赶紧着手做吧,事实上,手写功能并不难实现,大体就是全屏书写,定时发送handle消息,更新activity。

           实现手写功能的主要步骤:

                 1. 自己定义两个View,一个是TouchView,用于在上面绘图,还有一个是EditText,用于将手写的字显示在当中,而且,要将两个自己定义View通过FrameLayout帧式布局重叠在起,以实现全屏手写的功能。

                 2  在TouchView中实现写字,并截取画布中的字以Bitmap保存。

                 3. 设置定时器,利用handle更新界面。

           

            以下是实现的细节:

                1. 手写的界面设计:

                          如上图所看到的,和上节的画板界面一致,底部分选项菜单条,有5个选项,各自是调整画笔大小,画笔颜色,撤销,恢复,以及清空,对于这些功能,之后几节再实现。

                        布局文件activity_handwrite.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
         >
    
      	<FrameLayout
     	android:layout_width="fill_parent"
      	android:layout_height="wrap_content"
      	android:id="@+id/finger_layout"  
      	>
            
            <com.example.notes.LineEditText
            android:id="@+id/et_handwrite"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"
            android:fadingEdge="vertical"
            android:inputType="textMultiLine"
            android:gravity="top"
            android:textSize="20sp"
            android:layout_margin="5dp"
            android:focusable="true"
            android:lineSpacingExtra="10dp"
            android:textColor="#00000000"
            android:background="#00000000"
            
            />
            
     	  <com.example.notes.TouchView
            	android:id="@+id/touch_view"
            	android:layout_width="fill_parent"
            	android:layout_height="wrap_content"
            	android:background="@android:color/transparent" >
         </com.example.notes.TouchView>
      	
        
        </FrameLayout>
        <ImageView 
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:src="@drawable/line"
          android:layout_above="@+id/paintBottomMenu"
          />
       
        <GridView 
           android:id="@+id/paintBottomMenu" 
           android:layout_width="match_parent"
           android:layout_height="45dp"
           android:numColumns="auto_fit"
           android:background="@drawable/navigationbar_bg"
           android:horizontalSpacing="10dp"
           android:layout_alignParentBottom="true"
           ></GridView>
    
    </RelativeLayout>
    

                     能够看出,里面有两个自己定义view,而且通过FrameLayout重叠在一起。       

               

                    先来看com.example.notes.LineEditText,这个事实上和加入记事中的界面一样,就是自己定义EditText,而且在字的以下画一条线。

             LineEditText.java

    public class LineEditText extends EditText {
    	private Rect mRect;
    	private Paint mPaint;
    	
    	public LineEditText(Context context, AttributeSet attrs) {
    		// TODO Auto-generated constructor stub
    		super(context,attrs);
    		mRect = new Rect();
    		mPaint = new Paint();
    		mPaint.setColor(Color.GRAY);
    	}
    	
    	@Override
    	protected void onDraw(Canvas canvas) {
    		super.onDraw(canvas);
    		//得到EditText的总行数
    		int lineCount = getLineCount();
    		Rect r = mRect;
    		Paint p = mPaint;
    		//为每一行设置格式 
    		for(int i = 0; i < lineCount;i++){
    			//取得每一行的基准Y坐标,并将每一行的界限值写到r中
    			int baseline = getLineBounds(i, r);
    			//设置每一行的文字带下划线
    			canvas.drawLine(r.left, baseline+20, r.right, baseline+20, p);
    		}
    	}
    }

             还有一个就是com.example.notes.TouchView,实现了绘制,及定时更新界面的功能,详细看代码

             TouchView.java

    public class TouchView extends View {
     
    	private Bitmap  mBitmap,myBitmap;
    	private Canvas  mCanvas;
    	private Path    mPath;
    	private Paint   mBitmapPaint;
    	private Paint mPaint;
    	private Handler bitmapHandler;
    	GetCutBitmapLocation getCutBitmapLocation;
    	private Timer timer;
    	DisplayMetrics dm;
    	private int w,h;
    	public TouchView(Context context) {
            super(context);
            dm = new DisplayMetrics();
    		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
            w = dm.widthPixels;
            h = dm.heightPixels;
    		initPaint();
        }
        
    	public TouchView(Context context, AttributeSet attrs) {
    		super(context,attrs);
    		dm = new DisplayMetrics();
    		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
    		w = dm.widthPixels;
            h = dm.heightPixels;
    		initPaint();
    	}
    	//设置handler
    	public void setHandler(Handler mBitmapHandler){
    		bitmapHandler = mBitmapHandler;
    	}
    	
    	//初始化画笔,画布
    	private void initPaint(){
    		mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setDither(true);
            mPaint.setColor(0xFF00FF00);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStrokeWidth(15);  
            getCutBitmapLocation = new GetCutBitmapLocation();
            
            //画布大小 
            mBitmap = Bitmap.createBitmap(w, h, 
                Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBitmap);  //全部mCanvas画的东西都被保存在了mBitmap中
            
            mCanvas.drawColor(Color.TRANSPARENT);
            mPath = new Path();
            mBitmapPaint = new Paint(Paint.DITHER_FLAG);
            timer = new Timer(true);
    	}
    	
    
    	/**
    	 * 处理屏幕显示
    	 */
    	Handler handler = new Handler(){
    		public void handleMessage(Message msg) {
    			switch (msg.what) {			
    			case 1:	
    				myBitmap = getCutBitmap(mBitmap); 
    				Message message = new Message();
    				message.what=1;
    				Bundle bundle = new Bundle();;
    				bundle.putParcelable("bitmap",myBitmap);
    				message.setData(bundle);
    				bitmapHandler.sendMessage(message);
    				RefershBitmap();
    				break;
    			}
    			super.handleMessage(msg);
    		}
    	};
    	
    	/**
    	 * 发送消息给handler更新ACTIVITY		
    	 */
    	TimerTask task = new TimerTask() {
    		public void run() {
    			Message message = new Message();
    			message.what=1;
    			Log.i("线程", "来了");
    			handler.sendMessage(message);
    		}
    	};
    	
    	//分割画布中的字并返回
    	public Bitmap getCutBitmap(Bitmap mBitmap){
    		//得到手写字的四周位置,并向外延伸10px
    		float cutLeft = getCutBitmapLocation.getCutLeft() - 10;
    		float cutTop = getCutBitmapLocation.getCutTop() - 10;
    		float cutRight = getCutBitmapLocation.getCutRight() + 10;
    		float cutBottom = getCutBitmapLocation.getCutBottom() + 10;
    		
    		cutLeft = (0 > cutLeft ? 0 : cutLeft);
    		cutTop = (0 > cutTop ? 0 : cutTop);
    		
    		cutRight = (mBitmap.getWidth() < cutRight ? mBitmap.getWidth() : cutRight);
    		cutBottom = (mBitmap.getHeight() < cutBottom ? mBitmap.getHeight() : cutBottom);
    		
    		//取得手写的的高度和宽度 
    		float cutWidth = cutRight - cutLeft;
    		float cutHeight = cutBottom - cutTop;
    		
    		Bitmap cutBitmap = Bitmap.createBitmap(mBitmap, (int)cutLeft, (int)cutTop, (int)cutWidth, (int)cutHeight);
    		if (myBitmap!=null ) {
    			myBitmap.recycle();
    			myBitmap= null;
    		}
    		
    		return cutBitmap;
    	}
    	
    	//刷新画布
    	private void RefershBitmap(){
    		initPaint();
    		invalidate();
    		if(task != null)
    			task.cancel();
    	}
    	
        @Override
        protected void onDraw(Canvas canvas) {            
            canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);     //显示旧的画布       
            canvas.drawPath(mPath, mPaint);  //画最后的path
        }
        
        private float mX, mY;
        private static final float TOUCH_TOLERANCE = 4;
        
         //手按下时
        private void touch_start(float x, float y) {
            mPath.reset();//清空path
            mPath.moveTo(x, y);
            mX = x;
            mY = y;
            if(task != null)
            	task.cancel();//取消之前的任务
            task = new TimerTask() {
    			
    			@Override
    			public void run() {
    				Message message = new Message();
    				message.what=1;
    				Log.i("线程", "来了");
    				handler.sendMessage(message);
    			}
    		};
            getCutBitmapLocation.setCutLeftAndRight(mX,mY);
        }
        //手移动时
        private void touch_move(float x, float y) {
            float dx = Math.abs(x - mX);
            float dy = Math.abs(y - mY);
            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                mPath.quadTo(mX, mY, x, y);
                // mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);//源码是这样写的,但是我没有弄明确,为什么要这样?
                mX = x;
                mY = y;
                if(task != null)
                	task.cancel();//取消之前的任务
                task = new TimerTask() {
        			
        			@Override
        			public void run() {
        				Message message = new Message();
        				message.what=1;
        				Log.i("线程", "来了");
        				handler.sendMessage(message);
        			}
        		};
                getCutBitmapLocation.setCutLeftAndRight(mX,mY);
              
            }
        }
        //手抬起时
        private void touch_up() {
            //mPath.lineTo(mX, mY);
            mCanvas.drawPath(mPath, mPaint);
            mPath.reset();
            
            if (timer!=null) {
    			if (task!=null) {
    				task.cancel();
    				task = new TimerTask() {
    					public void run() {
    						Message message = new Message();
    						message.what = 1;
    						handler.sendMessage(message);
    					}
    				};
    				timer.schedule(task, 1000, 1000);				//2200秒后发送消息给handler更新Activity
    			}
    		}else {
    			timer = new Timer(true);
    			timer.schedule(task, 1000, 1000);					//2200秒后发送消息给handler更新Activity
    		}
            
        }
        
        //处理界面事件
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    touch_start(x, y);
                    invalidate(); //刷新
                    break;
                case MotionEvent.ACTION_MOVE:
                    touch_move(x, y);
                    invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    touch_up();
                    invalidate();
                    break;
            }
            return true;
        }
    
    }

            这里面的难点就是利用TimerTask和Handle来更新界面显示,须要在onTouchEvent的三个事件中都要通过handle发送消息来更新显示界面。

            

           接下来就是在activity里通过handle来得到绘制的字,并加入在editText中。

           关于配置底部菜单,以及顶部标题栏,这里不再赘述,直接怎样将绘制的字得到,并加入在edittext中:

          

             得到绘制字体的Bitmap

    	   //处理界面
    	    Handler handler = new Handler(){
    			@Override
    			public void handleMessage(Message msg) {
    				super.handleMessage(msg);
    				
    				Bundle bundle = new Bundle();
    				bundle = msg.getData();
    				Bitmap myBitmap = bundle.getParcelable("bitmap");	
    				InsertToEditText(myBitmap);
    			}
    	   };


              当中myBitmap就是取得的手写字,保存在Bitmap中,  InsertToEditText(myBitmap);是将该图片加入在edittext中,详细例如以下:

    	private LineEditText et_handwrite;      
    	et_handwrite = (LineEditText)findViewById(R.id.et_handwrite);

                       

    	   //将手写字插入到EditText中
    	    private void InsertToEditText(Bitmap mBitmap){
    	    		    	
    			int imgWidth = mBitmap.getWidth();
    			int imgHeight = mBitmap.getHeight();
    			//缩放比例
    			float scaleW = (float) (80f/imgWidth);
    			float scaleH = (float) (100f/imgHeight);
    			
    			Matrix mx = new Matrix();
    			//对原图片进行缩放
    			mx.postScale(scaleW, scaleH);
    			
    			mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, imgWidth, imgHeight, mx, true);
    			//将手写的字插入到edittext中
    			SpannableString ss = new SpannableString("1");
    			ImageSpan span = new ImageSpan(mBitmap, ImageSpan.ALIGN_BOTTOM);
    			ss.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
    			et_handwrite.append(ss);
    	   }
    	    

                这样,就实现了手写的功能,下节就实现手写字的撤销,恢复,以及清空的功能。

                     

                

                  

     

     

     

          

    转载于:https://www.cnblogs.com/blfshiye/p/4264408.html

    展开全文
  • namespace _5._29记事本 {  public partial class Form1 : Form  {  bool IsTextChanged = false;  string TextFileName = "";  public Form1()  {    InitializeComponent();  } ...

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.IO;

    namespace _5._29记事本
    {
        public partial class Form1 : Form
        {
            bool IsTextChanged = false;
            string TextFileName = "";
            public Form1()
            {
             
                InitializeComponent();
            }

            private void 新建ToolStripMenuItem_Click(object sender, EventArgs e)
            {
                txtTest.Text = "";
                IsTextChanged = false;
                TextFileName = "";//认为它是没有名字的
            }

            private void 打开OToolStripMenuItem_Click(object sender, EventArgs e)
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.Title = "打开文件";
                ofd.Filter = "文本文件|*.txt|所有文件|*.*";
               // ofd.InitialDirectory="c:\\";
                ofd.ShowDialog();
                if (ofd.ShowDialog() == DialogResult.OK)
                {
                   
                   //txtTest.Text=File.ReadAllLines(ofd.FileName);
                    ///第一步:定义一个文件流
                    FileStream fs = new FileStream(ofd.FileName, FileMode.Open, FileAccess.Read,FileShare.ReadWrite);
                    第二步:创建一个读取器
                    StreamReader sr = new StreamReader(fs,Encoding.Default);
                   //第三步:sr.ReadLine();读取一行
                     while(sr.EndOfStream==false)
                    {
                        string line = sr.ReadLine();///读取一行
                        txtTest.Text = txtTest.Text +line+"\r\n";
                    }
                    string TextFileName = ofd.FileName;
                    //txtTest.Text=sr.ReadToEnd();/读取到最(可以读全部)
                    /第四步:关闭读取器
                    sr.Close();
                    第五步:关闭文件流
                    fs.Close();
                    IsTextChanged = false;//人写代码时才把false改为true
                }
            }

            private void 退出XToolStripMenuItem_Click(object sender, EventArgs e)
            {
                this.Close();
                // Application.Exit();
            }

            private void 保存SToolStripMenuItem_Click(object sender, EventArgs e)
            {
                SaveText();
            }

            private void txtTest_TextChanged(object sender, EventArgs e)
            {
                IsTextChanged = true;
            }

            private void Form1_FormClosing(object sender, FormClosingEventArgs e)
            {
                if(IsTextChanged==true)
                {
                   DialogResult re= MessageBox.Show("文本内容已经改变,是否保存?","询问",MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question);
                    if(re==DialogResult.OK)
                    {
                        ///要保存
                        SaveText();
                    }
                    else if (re == DialogResult.No)
                    {
                        /不要保存
                    }
                    else
                    {
                    取消操作
                        e.Cancel = true;
                    }
                }
                int x = this.Location.X;
                int y = this.Location.Y;
                int w = this.Size.Width;
                int h = this.Size.Height;
                /设置一个文本文件,用来保存这些信息,叫 app.dll(就是一个库名)
                StreamWriter sw = new StreamWriter(Application.StartupPath+"\\app.dll",false);
                sw.WriteLine(x.ToString());
                sw.WriteLine(y.ToString());
                sw.WriteLine(w.ToString());
                sw.WriteLine(h.ToString());
                sw.WriteLine(txtTest.ForeColor.R);
                sw.WriteLine(txtTest.ForeColor.G);
                sw.WriteLine(txtTest.ForeColor.B);
                sw.Close();
            }
            protected void SaveText()
            {
                if (TextFileName == "")
                {
                    SaveFileDialog sfd = new SaveFileDialog();
                    sfd.Title = "打开文件";
                    sfd.Filter = "文本文件|*.txt|所有文件|*.*";
                    if (sfd.ShowDialog() == DialogResult.OK)
                    {
                        //第一步:声明文件流对象
                        FileStream fs = new FileStream(sfd.FileName, FileMode.Create);
                        //第二步:创建写入器
                        StreamWriter sw = new StreamWriter(fs, Encoding.Default);
                        //第三步:写操作
                        sw.Write(txtTest.Text);
                        /第四步:关闭写入器
                        sw.Close();
                        // 第五步:关闭文件流
                        fs.Close();
                        IsTextChanged = false;
                        TextFileName = sfd.FileName;
                    }
                }
                else
                {
                    //第一步:声明文件流对象
                    FileStream fs = new FileStream(TextFileName, FileMode.Create);
                    //第二步:创建写入器
                    StreamWriter sw = new StreamWriter(fs, Encoding.Default);
                    //第三步:写操作
                    sw.Write(txtTest.Text);
                    /第四步:关闭写入器
                    sw.Close();
                    // 第五步:关闭文件流
                    fs.Close();
                    IsTextChanged = false;
                }
            }

            private void 复制CToolStripMenuItem_Click(object sender, EventArgs e)
            {
                txtTest.Copy();
            }

            private void 剪贴TToolStripMenuItem_Click(object sender, EventArgs e)
            {
                txtTest.Cut();
            }

            private void 粘贴ToolStripMenuItem_Click(object sender, EventArgs e)
            {
                txtTest.Paste();
            }

            private void 查找FToolStripMenuItem_Click(object sender, EventArgs e)
            {
                int currPos = txtTest.SelectionStart;
                int findPos = txtTest.Text.IndexOf("中国", currPos);
                if (findPos != -1)
                {
                    ///说明找到了
                    txtTest.Select(findPos, 2);
                }
            }

            private void 字体ToolStripMenuItem_Click(object sender, EventArgs e)
            {
                FontDialog fd = new FontDialog();
                fd.Font = txtTest.Font;
                if (fd.ShowDialog() == DialogResult.OK)
                {
                    txtTest.Font = fd.Font;
                }
            }

            private void 字体颜色ToolStripMenuItem_Click(object sender, EventArgs e)
            {
                ColorDialog cd = new ColorDialog();
                cd.Color = txtTest.ForeColor;
                if(cd.ShowDialog()==DialogResult.OK)
                {
                    txtTest.ForeColor = cd.Color;
                }
            }

            private void 背景颜色ToolStripMenuItem_Click(object sender, EventArgs e)
            {
                ColorDialog cd = new ColorDialog();
                cd.Color = txtTest.BackColor;
                if (cd.ShowDialog() == DialogResult.OK)
                {
                    txtTest.BackColor = cd.Color;
                }
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                string appPath=Application.StartupPath+"\\app.dll";

                if (File.Exists(appPath))
                {
                    StreamReader sr = new StreamReader(appPath);
                    int x = Convert.ToInt32(sr.ReadLine());
                    int y = Convert.ToInt32(sr.ReadLine());
                    int w = Convert.ToInt32(sr.ReadLine());
                    int h = Convert.ToInt32(sr.ReadLine());
                    int r = Convert.ToInt32(sr.ReadLine());
                    int g = Convert.ToInt32(sr.ReadLine());
                    int b = Convert.ToInt32(sr.ReadLine());
                    this.Location=new Point(x,y);
                    this.Size = new Size(w,h);
                    txtTest.ForeColor = Color.FromArgb(r,g,b);
                    sr.Close();
                }
            }
        }
    }
    (5月30号,有待完善!)

    展开全文
  • android项目 之 记事本(6)----- 添加手写

    千次阅读 热门讨论 2014-09-18 20:24:48
    实现类似手机QQ白板中手写功能,而且是全屏手写,实时刷新

           想必大家都用过QQ的白板功能,里面主要有两项,一个是涂鸦功能,其实类似于上节的画板功能,而另一个就是手写,那记事本怎么能没有这个功能呢,今天就来为我们的记事本添加手写功能。

           先上图,看看效果:

           看了效果图,是不是心动了呢?那就赶紧着手做吧,其实,手写功能并不难实现,大体就是全屏书写,定时发送handle消息,更新activity。

           实现手写功能的主要步骤:

                 1. 自定义两个View,一个是TouchView,用于在上面画图,另一个是EditText,用于将手写的字显示在其中,并且,要将两个自定义View通过FrameLayout帧式布局重叠在起,以实现全屏手写的功能。

                 2  在TouchView中实现写字,并截取画布中的字以Bitmap保存。

                 3. 设置定时器,利用handle更新界面。

           

            下面是实现的细节:

                1. 手写的界面设计:

                          如上图所示,和上节的画板界面一致,底部分选项菜单栏,有5个选项,分别是调整画笔大小,画笔颜色,撤销,恢复,以及清空,对于这些功能,之后几节再实现。

                        布局文件activity_handwrite.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
         >
    
      	<FrameLayout
     	android:layout_width="fill_parent"
      	android:layout_height="wrap_content"
      	android:id="@+id/finger_layout"  
      	>
            
            <com.example.notes.LineEditText
            android:id="@+id/et_handwrite"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="vertical"
            android:fadingEdge="vertical"
            android:inputType="textMultiLine"
            android:gravity="top"
            android:textSize="20sp"
            android:layout_margin="5dp"
            android:focusable="true"
            android:lineSpacingExtra="10dp"
            android:textColor="#00000000"
            android:background="#00000000"
            
            />
            
     	  <com.example.notes.TouchView
            	android:id="@+id/touch_view"
            	android:layout_width="fill_parent"
            	android:layout_height="wrap_content"
            	android:background="@android:color/transparent" >
         </com.example.notes.TouchView>
      	
        
        </FrameLayout>
        <ImageView 
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:src="@drawable/line"
          android:layout_above="@+id/paintBottomMenu"
          />
       
        <GridView 
           android:id="@+id/paintBottomMenu" 
           android:layout_width="match_parent"
           android:layout_height="45dp"
           android:numColumns="auto_fit"
           android:background="@drawable/navigationbar_bg"
           android:horizontalSpacing="10dp"
           android:layout_alignParentBottom="true"
           ></GridView>
    
    </RelativeLayout>
    

                     可以看出,里面有两个自定义view,并且通过FrameLayout重叠在一起。       

               

                    先来看com.example.notes.LineEditText,这个其实和添加记事中的界面一样,就是自定义EditText,并且在字的下面画一条线。

             LineEditText.java

    public class LineEditText extends EditText {
    	private Rect mRect;
    	private Paint mPaint;
    	
    	public LineEditText(Context context, AttributeSet attrs) {
    		// TODO Auto-generated constructor stub
    		super(context,attrs);
    		mRect = new Rect();
    		mPaint = new Paint();
    		mPaint.setColor(Color.GRAY);
    	}
    	
    	@Override
    	protected void onDraw(Canvas canvas) {
    		super.onDraw(canvas);
    		//得到EditText的总行数
    		int lineCount = getLineCount();
    		Rect r = mRect;
    		Paint p = mPaint;
    		//为每一行设置格式 
    		for(int i = 0; i < lineCount;i++){
    			//取得每一行的基准Y坐标,并将每一行的界限值写到r中
    			int baseline = getLineBounds(i, r);
    			//设置每一行的文字带下划线
    			canvas.drawLine(r.left, baseline+20, r.right, baseline+20, p);
    		}
    	}
    }

             另一个就是com.example.notes.TouchView,实现了绘制,及定时更新界面的功能,具体看代码

             TouchView.java

    public class TouchView extends View {
     
    	private Bitmap  mBitmap,myBitmap;
    	private Canvas  mCanvas;
    	private Path    mPath;
    	private Paint   mBitmapPaint;
    	private Paint mPaint;
    	private Handler bitmapHandler;
    	GetCutBitmapLocation getCutBitmapLocation;
    	private Timer timer;
    	DisplayMetrics dm;
    	private int w,h;
    	public TouchView(Context context) {
            super(context);
            dm = new DisplayMetrics();
    		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
            w = dm.widthPixels;
            h = dm.heightPixels;
    		initPaint();
        }
        
    	public TouchView(Context context, AttributeSet attrs) {
    		super(context,attrs);
    		dm = new DisplayMetrics();
    		((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(dm);
    		w = dm.widthPixels;
            h = dm.heightPixels;
    		initPaint();
    	}
    	//设置handler
    	public void setHandler(Handler mBitmapHandler){
    		bitmapHandler = mBitmapHandler;
    	}
    	
    	//初始化画笔,画布
    	private void initPaint(){
    		mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setDither(true);
            mPaint.setColor(0xFF00FF00);
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeJoin(Paint.Join.ROUND);
            mPaint.setStrokeCap(Paint.Cap.ROUND);
            mPaint.setStrokeWidth(15);  
            getCutBitmapLocation = new GetCutBitmapLocation();
            
            //画布大小 
            mBitmap = Bitmap.createBitmap(w, h, 
                Bitmap.Config.ARGB_8888);
            mCanvas = new Canvas(mBitmap);  //所有mCanvas画的东西都被保存在了mBitmap中
            
            mCanvas.drawColor(Color.TRANSPARENT);
            mPath = new Path();
            mBitmapPaint = new Paint(Paint.DITHER_FLAG);
            timer = new Timer(true);
    	}
    	
    
    	/**
    	 * 处理屏幕显示
    	 */
    	Handler handler = new Handler(){
    		public void handleMessage(Message msg) {
    			switch (msg.what) {			
    			case 1:	
    				myBitmap = getCutBitmap(mBitmap); 
    				Message message = new Message();
    				message.what=1;
    				Bundle bundle = new Bundle();;
    				bundle.putParcelable("bitmap",myBitmap);
    				message.setData(bundle);
    				bitmapHandler.sendMessage(message);
    				RefershBitmap();
    				break;
    			}
    			super.handleMessage(msg);
    		}
    	};
    	
    	/**
    	 * 发送消息给handler更新ACTIVITY		
    	 */
    	TimerTask task = new TimerTask() {
    		public void run() {
    			Message message = new Message();
    			message.what=1;
    			Log.i("线程", "来了");
    			handler.sendMessage(message);
    		}
    	};
    	
    	//切割画布中的字并返回
    	public Bitmap getCutBitmap(Bitmap mBitmap){
    		//得到手写字的四周位置,并向外延伸10px
    		float cutLeft = getCutBitmapLocation.getCutLeft() - 10;
    		float cutTop = getCutBitmapLocation.getCutTop() - 10;
    		float cutRight = getCutBitmapLocation.getCutRight() + 10;
    		float cutBottom = getCutBitmapLocation.getCutBottom() + 10;
    		
    		cutLeft = (0 > cutLeft ? 0 : cutLeft);
    		cutTop = (0 > cutTop ? 0 : cutTop);
    		
    		cutRight = (mBitmap.getWidth() < cutRight ? mBitmap.getWidth() : cutRight);
    		cutBottom = (mBitmap.getHeight() < cutBottom ? mBitmap.getHeight() : cutBottom);
    		
    		//取得手写的的高度和宽度 
    		float cutWidth = cutRight - cutLeft;
    		float cutHeight = cutBottom - cutTop;
    		
    		Bitmap cutBitmap = Bitmap.createBitmap(mBitmap, (int)cutLeft, (int)cutTop, (int)cutWidth, (int)cutHeight);
    		if (myBitmap!=null ) {
    			myBitmap.recycle();
    			myBitmap= null;
    		}
    		
    		return cutBitmap;
    	}
    	
    	//刷新画布
    	private void RefershBitmap(){
    		initPaint();
    		invalidate();
    		if(task != null)
    			task.cancel();
    	}
    	
        @Override
        protected void onDraw(Canvas canvas) {            
            canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);     //显示旧的画布       
            canvas.drawPath(mPath, mPaint);  //画最后的path
        }
        
        private float mX, mY;
        private static final float TOUCH_TOLERANCE = 4;
        
         //手按下时
        private void touch_start(float x, float y) {
            mPath.reset();//清空path
            mPath.moveTo(x, y);
            mX = x;
            mY = y;
            if(task != null)
            	task.cancel();//取消之前的任务
            task = new TimerTask() {
    			
    			@Override
    			public void run() {
    				Message message = new Message();
    				message.what=1;
    				Log.i("线程", "来了");
    				handler.sendMessage(message);
    			}
    		};
            getCutBitmapLocation.setCutLeftAndRight(mX,mY);
        }
        //手移动时
        private void touch_move(float x, float y) {
            float dx = Math.abs(x - mX);
            float dy = Math.abs(y - mY);
            if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
                mPath.quadTo(mX, mY, x, y);
                // mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);//源代码是这样写的,可是我没有弄明白,为什么要这样?
                mX = x;
                mY = y;
                if(task != null)
                	task.cancel();//取消之前的任务
                task = new TimerTask() {
        			
        			@Override
        			public void run() {
        				Message message = new Message();
        				message.what=1;
        				Log.i("线程", "来了");
        				handler.sendMessage(message);
        			}
        		};
                getCutBitmapLocation.setCutLeftAndRight(mX,mY);
              
            }
        }
        //手抬起时
        private void touch_up() {
            //mPath.lineTo(mX, mY);
            mCanvas.drawPath(mPath, mPaint);
            mPath.reset();
            
            if (timer!=null) {
    			if (task!=null) {
    				task.cancel();
    				task = new TimerTask() {
    					public void run() {
    						Message message = new Message();
    						message.what = 1;
    						handler.sendMessage(message);
    					}
    				};
    				timer.schedule(task, 1000, 1000);				//2200秒后发送消息给handler更新Activity
    			}
    		}else {
    			timer = new Timer(true);
    			timer.schedule(task, 1000, 1000);					//2200秒后发送消息给handler更新Activity
    		}
            
        }
        
        //处理界面事件
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            float x = event.getX();
            float y = event.getY();
            
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    touch_start(x, y);
                    invalidate(); //刷新
                    break;
                case MotionEvent.ACTION_MOVE:
                    touch_move(x, y);
                    invalidate();
                    break;
                case MotionEvent.ACTION_UP:
                    touch_up();
                    invalidate();
                    break;
            }
            return true;
        }
    
    }

            这里面的难点就是利用TimerTask和Handle来更新界面显示,需要在onTouchEvent的三个事件中都要通过handle发送消息来更新显示界面。

            

           接下来就是在activity里通过handle来得到绘制的字,并添加在editText中。

           关于配置底部菜单,以及顶部标题栏,这里不再赘述,直接如何将绘制的字得到,并添加在edittext中:

          

             得到绘制字体的Bitmap

    	   //处理界面
    	    Handler handler = new Handler(){
    			@Override
    			public void handleMessage(Message msg) {
    				super.handleMessage(msg);
    				
    				Bundle bundle = new Bundle();
    				bundle = msg.getData();
    				Bitmap myBitmap = bundle.getParcelable("bitmap");	
    				InsertToEditText(myBitmap);
    			}
    	   };


              其中myBitmap就是取得的手写字,保存在Bitmap中,  InsertToEditText(myBitmap);是将该图片添加在edittext中,具体如下:

    	private LineEditText et_handwrite;      
    	et_handwrite = (LineEditText)findViewById(R.id.et_handwrite);

                       

    	   //将手写字插入到EditText中
    	    private void InsertToEditText(Bitmap mBitmap){
    	    		    	
    			int imgWidth = mBitmap.getWidth();
    			int imgHeight = mBitmap.getHeight();
    			//缩放比例
    			float scaleW = (float) (80f/imgWidth);
    			float scaleH = (float) (100f/imgHeight);
    			
    			Matrix mx = new Matrix();
    			//对原图片进行缩放
    			mx.postScale(scaleW, scaleH);
    			
    			mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, imgWidth, imgHeight, mx, true);
    			//将手写的字插入到edittext中
    			SpannableString ss = new SpannableString("1");
    			ImageSpan span = new ImageSpan(mBitmap, ImageSpan.ALIGN_BOTTOM);
    			ss.setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
    			et_handwrite.append(ss);
    	   }
    	    

                这样,就实现了手写的功能,下节就实现手写字的撤销,恢复,以及清空的功能。

                     

                

                  

     

     

     

          

    展开全文
  • Android 记事本

    2020-06-30 22:56:12
           ...  ... 通过之前的10节,已实现了记事本的大部分功能,有添加拍照,添加照片,添加录音,添加绘图,添加手写,另外细心的可以发现,底部菜单还有一...
  • 对于初学者,我们可能还没有用...其实出现这个问题是是存储为记事本时本身的格式问题。 解决的方案是:用记事本打开后,另存为别的文件,另存为时选择UTF-8模式另存到别的地方,再打开另存存的文件应该就可以了。
  • android项目 之 记事本(15) ----- 保存手写及绘图

    千次阅读 多人点赞 2014-11-07 23:50:24
    手写和绘图以图片的形式保存到指定目录下,并以时间作为文件名。
  • 多功能记事本

    2016-05-05 12:10:11
    一个多功能的记事本,支持手写,画板,插入图片,即时拍照,录音功能
  • 手写功能添加删除,恢复,清空的功能
  • android实现手写功能中的设置画笔的大小和颜色的功能,这样,就可以用不同颜色,不同大小的画笔书写字。
  • Java 编写的记事本

    2011-08-13 09:59:52
    自己手写的 没有可视化组件 基本实现打开 新建 保存功能 初学的可以参考
  • android记事本的demo在网上一搜一大堆,但是大神写的demo往往功能太多导致新手难以着手,很难啃得动;而一些新手写的demo又往往是东拼西凑,代码很多都是copy的别人的,直接放在项目里面用,也不知道代码有什么作用...
  • android 记事本demo!!!(listview与SQLite综合)

    万次阅读 多人点赞 2015-12-13 19:14:03
    android记事本的demo在网上一搜一大堆,但是大神写的demo往往功能太多导致新手难以着手,很难啃得动;而一些新手写的demo又往往是东拼西凑,代码很多都是copy的别人的,直接放在项目里面用,也不知道代码有什么作用...
  • JAVA/GUI程序之记事本

    2016-05-22 12:21:00
    自上半年JAVA课程结束后,再也没有看过JAVA了,最近不是很忙,又简单的看了看,本博客纯属记录学习过程,请大神们别笑,其中错误是难免的,毕竟是新手写的博客。下面就进入我们的正题吧,复习GUI时,就想到WINDOWS的...
  • 记事本源程序

    2014-04-17 11:33:53
    手写记事本程序,有android运行环境同学可用
  • java桌面课程设计——记事本

    千次阅读 2013-10-12 19:58:30
    以前也写过记事本,但是实现的功能不是太全,这次课程设计彻彻底底的把记事本主要功能实现包括:新建,打开,保存,另存,退出,打印,剪切,删除,复制,粘贴,全选,替换,查找,时间日期,自动换行,字体,帮助。...
  • android记事本的demo在网上一搜一大堆,但是大神写的demo往往功能太多导致新手难以着手,很难啃得动;而一些新手写的demo又往往是东拼西凑,代码很多都是copy的别人的,直接放在项目里面用,也不知道代码有什么作用...
  • 推荐个好用的安卓记事本便签软件 随笔记是简洁、清爽的记事本应用,也是豌豆荚设计奖获奖应用。 用它记笔记、写日记、保存灵感和想法、添加购物清单…随时随地轻松记录。支持文字、拍照、录音、手写、涂鸦等多种...
  • 用于记录当天工作安排,相当于以前用的黄色小贴纸,现在不用手写了,直接在电脑上写,安装后,移动小黄条到合适位置就能使用。
  • pink老师推荐的vue教程https://www.bilibili.com/video/BV1HE411e7vY?p=23的第二个示例,小黑记事本,因为没有资源所以都是手写的,欢迎大家来看
  • 记事本 具有简约设计的现代,... 多行手写支持。 内置Markdown实时预览。 内置差异查看器(预览您的更改)。 会话快照和多实例。 ******* :megaphone: Notepads App仍在积极开发中。 ******* 状态更新 捷径: Ctr
  • 简单的说就是一个比Windows自带的记事本更强大的记事本,他有语法高亮等特点,帮助我们手写代码,软件很小安装很简单。 你用的时候可以直接新建在电脑上新建记事本,然后选择用notep++打开写代码保存就行了, 当然你...
  • First是这个记事本的文件名,必须要带上.java后缀名第二步,开始手写一个简单的小程序写完保存以后,去掉记事本文件的txt后缀名改掉后缀名以后,文件格式会发生变化。第三步,打开dos窗口编译文件。win+R组合键打开...
  • 随手写小工具

    2012-10-14 00:41:27
    ( 隨手寫(最方便的手写记事本软件,支持彩信微博等)

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 153
精华内容 61
关键字:

手写记事本