JavaMe 编程连载(5) - 绘制文本框TextEdit

【问题描述】在JavaMe连载(3)-也说MVC设计模式一文中提到了一个TextEdit类,但没有给出具体实现,TextEdit是采用GameCanvas绘制的文本编辑器。本文结合实例给出实现的方法。

 

【原理】

1 运用Graphics、GameCanvas绘制文本框和光标。

2 检测到输入事件时,跳转到 高级界面->TextBox 。通过系统调用输入法完成输入。

3 将TextBox输入的值返回给TextEdit对象。

 

【设计模式】

这个过程有点类似装饰模式,实际上,实现输入的还是TextBox,只是给TextBox装饰了一下,形成了一个漂亮的外观。

 

【代码清单】

TextEdit.java

package com.token.view.components;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;

public class TextEdit extends GameCanvas
{
	private Font ft;
	
	public int width;
	public int height;
	
	public TextEdit(GameCanvas canvas) 
	{
		super(false);
		
	}
	
	//绘制文本框
	public void drawTextBox(GameCanvas canvas, Graphics graphics, String text, int x, int y, boolean cursorBlinkOn)
	{
		//System.out.println("draw");
		int padding = 4;
		int margin = 2;
		
		ft = Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_PLAIN,Font.SIZE_MEDIUM);
		graphics.setFont(ft);
		
		width = 3*canvas.getWidth()/5+2*padding;
		height = ft.getHeight()+2*padding;

		graphics.setColor(Color.frame);
		graphics.fillRect(x+1,y+1,width+margin,height+margin);
		
		graphics.setColor(Color.frameBg);
		graphics.drawRect(x, y,width, height);
		graphics.setColor(Color.background);
		graphics.fillRect(x+1, y+1,width-1,height-1);
		
		graphics.setColor(Color.text);
		graphics.drawString(text, x+padding, y+padding, Graphics.TOP|Graphics.LEFT);
		
		drawCursor(graphics, x+ft.stringWidth(text)+padding, y+padding, 1, ft.getHeight(), cursorBlinkOn);
		
		canvas.flushGraphics(x,y,width,height);
	}


	//绘制光标
	public void drawCursor(Graphics graphics, int x, int y, int width, int height, boolean cursorBlinkOn)
	{
		if(cursorBlinkOn)
		{
			ft = Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_PLAIN,Font.SIZE_MEDIUM);
			graphics.setFont(ft);
			graphics.setColor(0x0,0x0,0x0);
			graphics.drawLine(x+width,y,x+width,y+height);
		}
	}
}


PopUpTextBox.java

package com.token.view;

import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.TextBox;

import com.token.util.UIController;

public class PopUpTextBox extends TextBox {

	  private UIController controller;
	  protected String canvasText = "";
	  private Command okCommand;
	  private Command cancelCommand;
	  private String object_name = null;
	  private String editor = null;
	  private Object args_t[];
	  private Object[] args;
	  
	
	  public PopUpTextBox(UIController control, String title, String text, int maxsize, int constraints) 
	  {
		  super(title, text, maxsize, constraints);
		  controller = control;
		  
		  args = new Object[6];
		  
		  okCommand = new Command("确定", Command.OK, 1);
		  cancelCommand = new Command("取消", Command.CANCEL, 1);
		  
	      this.addCommand(okCommand);
	      this.addCommand(cancelCommand);
	      this.setCommandListener(new TextBoxListener());
	  }
	  
	  public void init(Object[] args)
	  {
		  object_name = ((String)args[0]!=null)?(String)args[0]:"";
		  editor = ((String)args[1]!=null)?(String)args[1]:"";
		  //System.out.println(object_name);
		  //System.out.println(editor);
		  args_t = args;
		  this.setString("");
	  }
		  
	  protected void closeTextBox(boolean update) {
	        if (update) canvasText = this.getString();
	        //System.out.println(canvasText);

	        if(object_name.equals("registScreen"))
	        {
		        if(editor.equals("regist_name"))
		        {
		        	if(args_t[3]!=""||args_t[3]!=null||args_t[4]!=""||args_t[4]!=null)
		        	{
		        		args[0] = object_name;
		        		args[1] = editor;
		        		args[2] = this.canvasText;
		        		args[3] = args_t[3];
		        		args[4] = args_t[4];
		        	}
		        	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT_BACK,args);
		        }
		        else if(editor.equals("regist_passwd"))
		        {
		        	if(args_t[2]!=""||args_t[2]!=null||args_t[4]!=""||args_t[4]!=null)
		        	{
		        		args[0] = object_name;
		        		args[1] = editor;
		        		args[2] = args_t[2];
		        		args[3] = this.canvasText;
		        		args[4] = args_t[4];
		        	}
		        	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT_BACK,args);
		        }
		        else if(editor.equals("regist_passwd_re"))
		        {
		        	if(args_t[2]!=""||args_t[2]!=null||args_t[3]!=""||args_t[3]!=null)
		        	{
		        		args[0] = object_name;
		        		args[1] = editor;
		        		args[2] = args_t[2];
		        		args[3] = args_t[3];
		        		args[4] = this.canvasText;
		        	}
		        	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT_BACK,args);
		        }
	        }
	        
	       //...
	}
	  
	 private class TextBoxListener implements CommandListener
	 { 
	    public void commandAction(Command command, Displayable disp)
	    {
	    	if(command==okCommand)
	    	{
	    		closeTextBox(true);
	    	}
			else if(command==cancelCommand)
			{
				closeTextBox(false);
			}
	    }
	}
}

 


UserRegist.java

package com.token.view;

import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import com.token.model.*;
import com.token.util.*;
import com.token.view.components.*;

public class UserRegist extends GameCanvas implements Runnable {

	private UIController controller;
	private Graphics graphics;
	
	private Font ft;
	
	private Menu menu;
	private Head head;
	private BackGroud backGroud;
	
	private UserDataRecord userRecord;

	private String title;
	
	private TextEdit textEdit_name;
	private TextEdit textEdit_passwd;
	private TextEdit textEdit_passwd_re;
	private int textEdit_name_x;
	private int textEdit_name_y;
	private int textEdit_passwd_x;
	private int textEdit_passwd_y;
	private int textEdit_passwd_re_x;
	private int textEdit_passwd_re_y;
	private int currentlySelectedIndex = 0;
	
	private String username;
	private String passwd;
	private String passwd_re;
	
	long caretBlinkDelay = 500L;
	long lastCaretBlink = 0;
	private String object_name;
	private String editor;
	private boolean cursorBlinkOn1;
	private boolean cursorBlinkOn2;
	private boolean cursorBlinkOn3;
	
	private int width;
	private int height;
	
	public UserRegist(UIController control) 
	{
		super(false);
		this.controller=control;
		this.title = "用户注册";
		setFullScreenMode(true);
		graphics = getGraphics();
		
		width = getWidth();
		height = getHeight();
		
		menu = new Menu(this);
		head = new Head(this);
		backGroud = new BackGroud(this);
		
		userRecord = new UserDataRecord();
		
		textEdit_name = new TextEdit(this);
		textEdit_passwd = new TextEdit(this);
		textEdit_passwd_re = new TextEdit(this);
	}

	public void show(Object[] args) {
		// TODO Auto-generated method stub
		setFullScreenMode(true);
		
		object_name = ((String)args[0]!=null)?(String)args[0]:"";
		editor = ((String)args[1]!=null)?(String)args[1]:"";
		username = ((String)args[2]!=null)?(String)args[2]:"";
		passwd = ((String)args[3]!=null)?(String)args[3]:"";
		passwd_re = ((String)args[4]!=null)?(String)args[4]:"";
		
		if(editor.equals("regist_name"))
		{
			cursorBlinkOn1 = true;
			cursorBlinkOn2 = false;
			cursorBlinkOn3 = false;
			currentlySelectedIndex =0;
		}
		else if(editor.equals("regist_passwd"))
		{
			cursorBlinkOn1 = false;
			cursorBlinkOn2 = true;
			cursorBlinkOn3 = false;
			currentlySelectedIndex =1;
		}
		else if(editor.equals("regist_passwd_re"))
		{
			cursorBlinkOn1 = false;
			cursorBlinkOn2 = false;
			cursorBlinkOn3 = true;
			currentlySelectedIndex =2;
		}
		
		//System.out.println(object_name);
		//System.out.println(editor);
		draw();
		redraw();
	}

	public void draw()
	{
		//clearScreen();
		backGroud.drawBackGroud(this, graphics);
		head.drawHead(this,graphics,this.title);
		menu.drawMenu(this,graphics,"下一步","退出");
		drawBody();
	}

	private void redraw()
	{
		switch(currentlySelectedIndex)
		{
			case 0:
			{
				cursorBlinkOn2 = false;
				cursorBlinkOn3 = false;
				editor = "regist_name";
				break;
			}
			case 1:
			{
				cursorBlinkOn1 = false;
				cursorBlinkOn3 = false;
				editor = "regist_passwd";
				break;
			}
			case 2:
			{
				cursorBlinkOn1 = false;
				cursorBlinkOn2 = false;
				editor = "regist_passwd_re";
				break;
			}
			default:;
		}
		
		textEdit_name.drawTextBox(this, graphics, username, textEdit_name_x, textEdit_name_y, cursorBlinkOn1);
		textEdit_passwd.drawTextBox(this, graphics, passwd, textEdit_passwd_x, textEdit_passwd_y, cursorBlinkOn2);
		textEdit_passwd.drawTextBox(this, graphics, passwd_re, textEdit_passwd_re_x, textEdit_passwd_re_y, cursorBlinkOn3);
		textEdit_name.flushGraphics();
	}

	public void drawBody()
	{
		int margin =5;
		ft = Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_BOLD,Font.SIZE_LARGE);
		
		String info = "用户名:\n";
		String info_wrap1[] = StringDealMethod.format(info, width-10, ft);
		
		graphics.setFont(ft);
		graphics.setColor(Color.text);
		for(int i=0; i<info_wrap1.length; i++)
		{
			graphics.drawString(info_wrap1[i],5, (i) * ft.getHeight()+40, Graphics.TOP|Graphics.LEFT);
		}
		
		textEdit_name_x = 5;
		textEdit_name_y = info_wrap1.length * ft.getHeight()+40;
		textEdit_name.drawTextBox(this, graphics, username, textEdit_name_x, textEdit_name_y, cursorBlinkOn1);
		
		info = "用户密码:\n";
		String info_wrap2[] = StringDealMethod.format(info, width-10, ft);
	
		graphics.setFont(ft);
		graphics.setColor(Color.text);
		for(int i=0; i<info_wrap2.length; i++)
		{
			graphics.drawString(info_wrap2[i],5, (i+info_wrap1.length) * ft.getHeight()+textEdit_name.height+margin+40, Graphics.TOP|Graphics.LEFT);
		}
		
		textEdit_passwd_x = 5;
		textEdit_passwd_y = (info_wrap1.length+info_wrap2.length) * ft.getHeight()+textEdit_name.height+margin+40;
		textEdit_passwd.drawTextBox(this, graphics, passwd, textEdit_passwd_x, textEdit_passwd_y, cursorBlinkOn2);
	
		info = "密码确认:\n";
		String info_wrap3[] = StringDealMethod.format(info, width-10, ft);
	
		graphics.setFont(ft);
		graphics.setColor(Color.text);
		for(int i=0; i<info_wrap3.length; i++)
		{
			graphics.drawString(info_wrap3[i],5, (i+info_wrap1.length+info_wrap2.length) * ft.getHeight()+textEdit_name.height+textEdit_passwd.height+2*margin+40, Graphics.TOP|Graphics.LEFT);
		}
		
		textEdit_passwd_re_x = 5;
		textEdit_passwd_re_y = (info_wrap1.length+info_wrap2.length+info_wrap3.length) * ft.getHeight()+textEdit_name.height+textEdit_passwd.height+2*margin+40;
		textEdit_passwd_re.drawTextBox(this, graphics, passwd_re, textEdit_passwd_re_x, textEdit_passwd_re_y, cursorBlinkOn3);
	
		
	}

	public void clearScreen()
	{
		graphics.setColor(0xff,0xff,0xff);
		graphics.fillRect(0, 0, width, height);
	}

	public void checkTimeStamp()
	{
		long currentTime = System.currentTimeMillis();
		//System.out.println("1");
		if(lastCaretBlink + caretBlinkDelay < currentTime)
		{
			//System.out.println("2");
			if(editor.equals("regist_name"))
			{
				cursorBlinkOn1 =! cursorBlinkOn1;
				cursorBlinkOn2 = false;
				cursorBlinkOn3 = false;
			}
			else if(editor.equals("regist_passwd"))
			{
				cursorBlinkOn1 = false;
				cursorBlinkOn2 =! cursorBlinkOn2;
				cursorBlinkOn3 = false;
			}
			else if(editor.equals("regist_passwd_re"))
			{
				cursorBlinkOn1 = false;
				cursorBlinkOn2 = false;
				cursorBlinkOn3 =! cursorBlinkOn3;
			}
			lastCaretBlink = currentTime;
		}
	}

	public void run()
	{
		//System.out.println("run");
		while(true)
		{
			checkTimeStamp();
			
			redraw();
			try	
			{
				synchronized(this)
				{
					//System.out.println("3");
					wait(50L);
				}			
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
			
		}
	}

	protected void keyPressed(int keyCode)
	{
		switch(keyCode)
		{
			case KeyID.SOFT_RIGHT:
			{
				controller.handleEvent(UIController.EventID.EVENT_EXIT,null);
				break;
			}
			case KeyID.SOFT_LEFT:
			{
				if(username!="" && passwd!=""&&passwd_re!="")
				{
					if(passwd.equals(passwd_re))
					{
						
						
						userRecord.db_deleteAllRecord();
						if(userRecord.db_getRecord(1)==null)
						{
							UserDataItem userItem = new UserDataItem(1,(username+","+passwd).getBytes());
							userRecord.db_addRecord(userItem);
							userItem = null;
							System.gc();
						}
						
						String update = "start";
						Object [] args = {"activeScreen", null, update};
						controller.handleEvent(UIController.EventID.EVENT_NEXT_ACTIVE_TOKEN_SCREEN,args);
					}
				}
				break;
			}
			case KeyID.KEY_EDIT:
			case KEY_NUM0:
			case KEY_NUM1:
			case KEY_NUM2:
			case KEY_NUM3:
			case KEY_NUM4:
			case KEY_NUM5:
			case KEY_NUM6:
			case KEY_NUM7:
			case KEY_NUM8:
			case KEY_NUM9:
			{
				//System.out.println(editor);
				Object[] args = {object_name,editor,username,passwd,passwd_re};
				controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT,args);
				break;
			}
			default:;
		}
			
		keyCode = getGameAction(keyCode);
		switch(keyCode)
		{
			case UP:
			case LEFT:
			{
				currentlySelectedIndex--;
				if(currentlySelectedIndex<0)
				{
					currentlySelectedIndex=0;
				}
				else
				{
					redraw();
				}
				break;
			}
			case DOWN:
			case RIGHT:
			{
				currentlySelectedIndex++;
				if(currentlySelectedIndex>2)
				{
					currentlySelectedIndex=2;
				}
				else
				{
					redraw();
				}
				
				break;
			}
		}
	}

}


【分析】

1 文本框的绘制(TextEdit.java)

需要传递GameCanvas、Graphics对象,实现绘图,策略是谁使用,谁传递该参数。此外需要床底文本框左上角坐标(x,y)以及控制光标闪烁的变量cursorBlinkOn。

public void drawTextBox(GameCanvas canvas, Graphics graphics, String text, int x, int y, boolean cursorBlinkOn)
{
	//System.out.println("draw");
	int padding = 4;
	int margin = 2;
	
	ft = Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_PLAIN,Font.SIZE_MEDIUM);
	graphics.setFont(ft);
	
	width = 3*canvas.getWidth()/5+2*padding;
	height = ft.getHeight()+2*padding;

	graphics.setColor(Color.frame);
	graphics.fillRect(x+1,y+1,width+margin,height+margin);
		
	graphics.setColor(Color.frameBg);
	graphics.drawRect(x, y,width, height);
	graphics.setColor(Color.background);
	graphics.fillRect(x+1, y+1,width-1,height-1);
		
	graphics.setColor(Color.text);
	graphics.drawString(text, x+padding, y+padding, Graphics.TOP|Graphics.LEFT);
		
	drawCursor(graphics, x+ft.stringWidth(text)+padding, y+padding, 1, ft.getHeight(), cursorBlinkOn);
		
	canvas.flushGraphics(x,y,width,height);
}


2 绘制光标(TextEdit.java)

public void drawCursor(Graphics graphics, int x, int y, int width, int height, boolean cursorBlinkOn)
{
	if(cursorBlinkOn)
	{
		ft = Font.getFont(Font.FACE_PROPORTIONAL,Font.STYLE_PLAIN,Font.SIZE_MEDIUM);
		graphics.setFont(ft);
		graphics.setColor(0x0,0x0,0x0);
		graphics.drawLine(x+width,y,x+width,y+height);
	}
}


3 实现光标闪烁

光标闪烁的实现需要用到线程,在UIController.java类中,需要绘制文本框的视图类,需要实现线程接口。

UIController.java

case EventID.EVENT_NEXT_USER_REGIST_SCREEN:
case EventID.EVENT_USER_REGIST_EDIT_BACK:
{
    	    reg.show(args);
    	    Thread thread = new Thread(reg);
    	    thread.start();
    	    midlet.setCurrent(reg);
    	    break;
}

 

UserRegist.java

public void checkTimeStamp()
{
	long currentTime = System.currentTimeMillis();
	//System.out.println("1");
	if(lastCaretBlink + caretBlinkDelay < currentTime)
	{
		//System.out.println("2");
		if(editor.equals("regist_name"))
		{
			cursorBlinkOn1 =! cursorBlinkOn1;
			cursorBlinkOn2 = false;
			cursorBlinkOn3 = false;
		}
		else if(editor.equals("regist_passwd"))
		{
			cursorBlinkOn1 = false;
			cursorBlinkOn2 =! cursorBlinkOn2;
			cursorBlinkOn3 = false;
		}
		else if(editor.equals("regist_passwd_re"))
		{
			cursorBlinkOn1 = false;
			cursorBlinkOn2 = false;
			cursorBlinkOn3 =! cursorBlinkOn3;
		}
		lastCaretBlink = currentTime;
	}
}

public void run()
{
	//System.out.println("run");
	while(true)
	{
		checkTimeStamp();
		
		redraw();
		try	
		{
			synchronized(this)
			{
				//System.out.println("3");
				wait(50L);
			}			
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
			
	}
}

 

4 调用高级界面TextBox子类PopUpTextBox

调用时,将调用对象名、编辑对象名、以及编辑框参数传递给PopUpTextBox对象(一定要有,目的是保存编辑框的值,否则多次调用返回时,不同编辑框的值被刷新为空了)

UserRegist.java(KeyPressed)

case KeyID.KEY_EDIT:
case KEY_NUM0:
case KEY_NUM1:
case KEY_NUM2:
case KEY_NUM3:
case KEY_NUM4:
case KEY_NUM5:
case KEY_NUM6:
case KEY_NUM7:
case KEY_NUM8:
case KEY_NUM9:
{
	//System.out.println(editor);
	Object[] args = {object_name,editor,username,passwd,passwd_re};
	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT,args);
	break;
}


UIController.java

case EventID.EVENT_USER_REGIST_EDIT:
{
  	 textBox.init(args);
    	 midlet.setCurrent(textBox);
   	 break;
}


5 PopUpTextBox参数的接收

public void init(Object[] args)
{
	 object_name = ((String)args[0]!=null)?(String)args[0]:"";
	 editor = ((String)args[1]!=null)?(String)args[1]:"";
	 //System.out.println(object_name);
	 //System.out.println(editor);
	 args_t = args;
	 this.setString("");
}

 

6 PopUpTextBox返回输入法输入的值

 if (update) canvasText = this.getString();


7  PopUpTextBox输入值处理

依据调用的对象,以及编辑对象,对输入的值进行处理,传递给父对象编辑框

if(object_name.equals("registScreen"))
{
        if(editor.equals("regist_name"))
        {
	        	if(args_t[3]!=""||args_t[3]!=null||args_t[4]!=""||args_t[4]!=null)
	        	{
	        		args[0] = object_name;
	        		args[1] = editor;
	        		args[2] = this.canvasText;
	        		args[3] = args_t[3];
	        		args[4] = args_t[4];
	        	}
	        	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT_BACK,args);
	}
	else if(editor.equals("regist_passwd"))
	{
	        	if(args_t[2]!=""||args_t[2]!=null||args_t[4]!=""||args_t[4]!=null)
	        	{
	        		args[0] = object_name;
	        		args[1] = editor;
	        		args[2] = args_t[2];
	        		args[3] = this.canvasText;
	        		args[4] = args_t[4];
	        	}
	        	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT_BACK,args);
	}
	else if(editor.equals("regist_passwd_re"))
	{
	        	if(args_t[2]!=""||args_t[2]!=null||args_t[3]!=""||args_t[3]!=null)
	        	{
	        		args[0] = object_name;
	        		args[1] = editor;
	        		args[2] = args_t[2];
	        		args[3] = args_t[3];
	        		args[4] = this.canvasText;
	        	}
	        	controller.handleEvent(UIController.EventID.EVENT_USER_REGIST_EDIT_BACK,args);
	}
}

 

8 输入值的显示

(1) 新建对象

private TextEdit textEdit_name;
textEdit_name = new TextEdit(this);

(2) 接受输入的参数

object_name = ((String)args[0]!=null)?(String)args[0]:"";
editor = ((String)args[1]!=null)?(String)args[1]:"";
username = ((String)args[2]!=null)?(String)args[2]:"";
passwd = ((String)args[3]!=null)?(String)args[3]:"";
passwd_re = ((String)args[4]!=null)?(String)args[4]:"";

 

(3) 光标控制,定位到编辑对象,控制编辑对象的光标闪烁(run方法)

private void redraw()
{
	switch(currentlySelectedIndex)
	{
		case 0:
		{
			cursorBlinkOn2 = false;
			cursorBlinkOn3 = false;
			editor = "regist_name";
			break;
		}
		case 1:
		{
			cursorBlinkOn1 = false;
			cursorBlinkOn3 = false;
			editor = "regist_passwd";
			break;
		}
		case 2:
		{
			cursorBlinkOn1 = false;
			cursorBlinkOn2 = false;
			editor = "regist_passwd_re";
			break;
		}
		default:;
	}
//...

}

 

(4) 编辑框的绘制

private void redraw()
{
	...		
	textEdit_name.drawTextBox(this, graphics, username, textEdit_name_x, textEdit_name_y, cursorBlinkOn1);
	textEdit_passwd.drawTextBox(this, graphics, passwd, textEdit_passwd_x, textEdit_passwd_y, cursorBlinkOn2);
	textEdit_passwd.drawTextBox(this, graphics, passwd_re, textEdit_passwd_re_x, textEdit_passwd_re_y, cursorBlinkOn3);
	textEdit_name.flushGraphics();
}

实现的效果如图1所示:

图1 编辑框效果

 

【参考文献】

(1) http://www.developer.nokia.com/Community/Wiki/Custom_Text_Input_in_Java_ME

(2) j2me跳转到高级界面获得输入法

(3) http://www.iteye.com/topic/394113

(4) http://topic.csdn.net/t/20060906/09/5001597.html

 

 

 

转载请标明出处,仅供学习交流,勿用于商业目的

Copyright @ http://blog.csdn.net/tandesir

 





 

 

 


 


 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值