將手機螢幕當作便條紙可以在上面畫圖。
一、畫圖實作
圖構成的邏輯:
畫圖由多條線構成一張圖
而兩個以上的點構成一條線
實作:
1.宣告LinkedList 裡面存放多條線(多個HashMap物件)
2.宣告HashMap代表一個線條,HashMap存放線條的顏色、粗細,以及構成線條的座標
3.HashMap裡面其中一欄位存放LinkedList存放多個座標,每一個座標以HashMap姿態放進LinkedList中
(一條線由多個座標連起來所構成)
因此實作LinkedList為容器存放多個座標,
接著利用GestureDetector以及onTouchEvent onDraw onDown來實作將HashMap放進LinkedList
或者取出的動作啦
ScrollView:ScrollView也是layout一種,只限定vertical的方向。當scrollview裡面的元件超過螢幕顯示範圍,可以滾動方式取得元件。不可以放在LinearLayout之內。
自訂View:新增.java類別,類別必須繼承(extends View)。
且必須有建構式,本例子的建構式接收Context,AttributeSet
所謂Context就是指向parent對象的指標(或參考),受到parent控制
通常Context指向Activity
Context可以理解成程式(物件)所在的環境(狀態)
Context裡面存放Activity各式各樣的資訊
首先,準備好painel view
public class MainPanel extends View
在建構式宣告畫筆Paint
以及手勢偵測物件,告知gesturedetector所在context以及listener
在建構式外面宣告傾聽者類別繼承手勢傾聽者
new GestureDetector(context,MyListenter)
private void MyListener extends SimpleOnGestureListener{}
GestureListener要override OnDown 以及onScroll method
MainPanel(View) 要override onTouchEvent 以及onDraw method
範例程式:
public class MainPanel extends View {
private Paint p;// 畫筆
private GestureDetector gd;
private Context context;
private HashMap<String, Object> line;//
private LinkedList<HashMap<String, Object>> lines, redos;
private int size;// 筆畫粗細
private boolean isInit;
private boolean isDrawing;
public MainPanel(Context c, AttributeSet attrs) {
super(c, attrs);
context = c;// 從mainActivity來的?
//Log.i("context",context.toString());
//context指的是MainActivity@416a87d0
size = 10;
p = new Paint();
p.setColor(Color.MAGENTA);
p.setStrokeWidth(size);
gd = new GestureDetector(context, new MyListener());
// 手勢偵測物件,偵測碰觸螢幕的事件
setBackgroundColor(Color.TRANSPARENT);// View的顏色與parent一樣
}
private class MyListener extends SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
//onTouchEvent return gd.onTouchEvent(event)且action_down 會觸發這裡
//Log.i("Sing","onDown");
//onDown表示線條起點
float x = e.getX(), y = e.getY();
//手指碰到的座標
line.put("color", p.getColor());
line.put("size", (int)p.getStrokeWidth());
LinkedList<HashMap<String,Float>> lxy = new LinkedList();
HashMap<String,Float> xy = new HashMap();
xy.put("x", x);
xy.put("y", y);
lxy.add(xy);//HashMap放進Linklist
//將起始座標放入lxy[0]
line.put("line", lxy);//起始座標
// return super.onDown(e);
return true;
//retrun true會持續觸發touchevent 以及onScroll
}
// @Override
// public boolean onScroll(MotionEvent e1, MotionEvent e2,
// float distanceX, float distanceY) {
// Log.i("Sing","onScroll");
//
// return super.onScroll(e1, e2, distanceX, distanceY);
// }
}//GuestureDetector
//初始化
private void init(){
lines = new LinkedList<HashMap<String,Object>>();
redos= new LinkedList<HashMap<String,Object>>();
isDrawing = false;
isInit =true;
}
@Override
protected void onDraw(Canvas canvas) {
Log.i("Sing","onDraw");
super.onDraw(canvas);
if(!isInit) init();
//初始化,因為onDraw相當於View的main,
//但是有時會自動執行多次 所以就用init()初始化
//第一次進入這裡不會在View上畫出東西
//第一次進入畫面不會跑進foreachloop
for(HashMap<String,Object> line : lines){
// p.setColor(Color.BLACK);
// p.setStrokeWidth(3);
//所以這邊可以用條件判斷,當line裡面lxy的index符合某條件時
//更改線條顏色
p.setColor((Integer)line.get("color"));
//Log.i("Conan"," "+(Integer)line.get("color"));
p.setStrokeWidth((Integer)line.get("size"));
// Log.i("Conan"," "+(Integer)line.get("size"));
LinkedList<HashMap<String,Float>> lxy = (LinkedList)line.get("line");
//第一次treace程式碼:line存放兩個座標了
for(int i=1; i<lxy.size();i++){
HashMap<String,Float> xy0 = lxy.get(i-1);
//取得linkedlist 裡面物件 ,節點1
HashMap<String,Float> xy1 = lxy.get(i);
//節點2
canvas.drawLine(xy0.get("x"), xy0.get("y"), xy1.get("x"), xy1.get("y"), p);
// 畫線
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Log.i("Sing","onTouchEvent");
if(!isDrawing){
//之前尚未開始畫圖,進入這裡進行初始化
//第二次treace:新增第二條線
line = new HashMap();
lines.add(line);//line是一條線,lines放多條線的LinkedList
//第二次trace到這,在lines[1]加上line HashMap物件
//還沒設定線條顏色與粗細
// Log.i("YYP", "" + lines.size());
//lines放n條線
isDrawing = true;
return gd.onTouchEvent(event);//第一次onTouchEvent後跳去onDown
}
else if(isDrawing && event.getAction()==MotionEvent.ACTION_UP)
{//正在畫圖 手指離開螢幕
isDrawing = false ;
return false;
}
else
{//isDrawing = true event.getAction()==MotionEvent.Action_Move
//正在畫圖
LinkedList<HashMap<String,Float>> lxy = (LinkedList)line.get("line");
//這裡的line跟onDwon的line是同一個
//第二次trace到這邊:line裡面已經有兩個座標,
HashMap<String,Float> xy = new HashMap();
xy.put("x", event.getX());//線的終點
xy.put("y", event.getY());
//第二次trace到這邊:增加第三座標
lxy.add(xy);
//第一次trace將座標放入lxy[1]
//第二次trace到這 ,座標放入lxy[2]
postInvalidate();//呼叫ondraw
return gd.onTouchEvent(event);
}
// String str=event.toString();
// Log.i("Sing",str);
// int action = event.getAction();
// cordinateX = (int) event.getX();
// cordinateY = (int) event.getY();
// switch (action) {
// // 按下那一刻的動作
// case MotionEvent.ACTION_DOWN:
// Log.v("Sing", "ACTION_DOWN");
// break;
// // 移動的動作
// case MotionEvent.ACTION_MOVE:
// Log.v("Sing", "ACTION_MOVE");
// break;
// // 抬起的動作
// case MotionEvent.ACTION_UP:
// Log.v("Sing", "ACTION_UP");
//// postInvalidate();//好像開啟另一條thread呼叫onDraw吧?
// break;
// }
// return super.onTouchEvent(event);
// return true;
}
}
二、開新檔案的功能:
以上完成之後,手指在螢幕上畫圖表示
將線條座標一一塞入LinkedList裡面。
開新檔案就是將LinkedList裡面的物件(線條、座標)清空
最快的方法重新new一個LinkedList物件,讓line指向新的物件
這樣就達到開新檔案了。
在MainPanel裡面宣告
void doInit(){
isInit=false;
postInvalidate;
//執行上面的onDraw
}
在MainActivity
宣告view變數
private MainPanel mp;
mp= (MainPanel)findViewById(R.id.mp);
//若沒寫這行會出現nullPointException
將mp.doInit();寫在btnOnClick事件裡。
三、復原、重做
以一個線條為單位復原重作。void undo() {
//復原表示將LinkedList的元素逐一刪掉
if (!lines.isEmpty()) {
//lines.removeLast();
//
// lines裡面將線條移除
redos.add(lines.removeLast());
//刪掉的元素放進redo
postInvalidate();
}
}
void redo(){
if((redos!=null)&&(redos.size()>0)){
lines.add(redos.removeLast());
//將redo的東西重新放入lines
postInvalidate();
}
}
四、自訂線條顏色(color picker實作)
取用別人寫好的code當作自己的函式庫libray引用別人寫好的函式庫
1.
首先下載
android color picker
整個project程式碼
2. import android-color-picker
3.import之後在android-color-picker專案點右鍵,選properties
之後選擇"android" 之後將"is Libray"打勾 之後點apply並關閉視窗
4.回到原本project
在原本project點右鍵跟上面一樣選properties 選android
之後點add
將android-color-picker加入
這樣一來可以將自己/別人寫的code當作函式庫引用了
private void colorPicker(){
// initialColor is the initially-selected color to be shown in the rectangle on the left of the arrow.
// for example, 0xff000000 is black, 0xff0000ff is blue. Please be aware of the initial 0xff which is the alpha. AmbilWarnaDialog dialog = new AmbilWarnaDialog(this,mp.getColor, new OnAmbilWarnaListener() { @Override public void onOk(AmbilWarnaDialog dialog, int color) { // color is the color selected by the user.
mp.setColor(color);
} @Override public void onCancel(AmbilWarnaDialog dialog) { // cancel was selected by the user } });}
相關步驟以及整個code下載來源如下:
https://code.google.com/p/android-color-picker/
五、儲存檔案
首先去mainifest.xml設定權限
android.permission.WRITE_EXTERNAL_STORAGE
允許程式可以將資料儲存到手機上面去
接著利用MainPanel extends View物件的setDrawingCacheEnabled(true)
mp.setDrawingCacheEnabled(true);
讓onDraw的資訊可以快取在記憶體
不寫這行,下面程式會出現NotFoundException之類的字眼
Bitmap物件在記憶體中抓取不到資料
private void save(){}
FileOutputStream fout;
try{
fout = new FileOutputStream("/mnt/sdcard/name.png");
//指定輸出到內部儲存空間
Bitmap bmp = mp.getDrawingCache();
//Bitmap物件 接收快取記憶體裡的資料
bmp.compress(Compress.PNG,100,fout);
//輸出格式 輸出品質 輸出目的
fout.flush();
fout.close();
Toast.makeText(this, "saveOK", Toast.LENGTH_SHORT).show();
}catch(Exception e){
Log.i("Exception",e.toString());
}
六、自訂線條大小
SeekBar實作
private SeekBar seekbar;
seekbar = (SeekBar)findViewById(R.id.seekbar);
seekbar.setProgress(mp.getSize());
//設定seekbar初始值
seekbar.setMax(10);
//設定seekbar最大值
seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){
@Override
public void onProgressChanged(SeekBar arg0, int progress, boolean fromUser) {
//progress:seekbar的值
mp.setSize(progress);
}
@Override
public void onStartTrackingTouch(SeekBar arg0) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}});
int getSize(){
return (int)p.getStrokeWidth();
}
void setSize(int size){
p.setStrokeWidth(size);
}
七、自訂歡迎畫面
Application Node新增Activity
在Activity底下新增
intent filter將其他activity的intent filter刪掉
這樣可以依照自己意思指定起始畫面了
PS:
private Resource res;
res = getResources();//從Context取得Resource物件
//Resource物件可以在.java檔案取得該專案的xml資源設定值
Toast.makeText(this, res.getString(R.string.btn_save), Toast.LENGTH_SHORT).show();
//re.getString(R.string.btn_save); 印出string.xml中所對應的字串
去看values/string.xml有這樣標籤
<string name="btn_save">Save</string>
沒有留言:
張貼留言