65.9K
CodeProject 正在变化。 阅读更多。
Home

为 Android 创建位图 XY 图表/绘图

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.65/5 (22投票s)

2010年7月27日

GPL3

4分钟阅读

viewsIcon

130239

downloadIcon

110797

使用 Canvas/Bitmap 获取 XY 绘图

引言

绘制 XY 集是一个非常常见的任务,本文概述了在 Android 平台上实现此类模块的主要步骤。这与之前发布的类似的 BlackBerry 文章相符。

在 Android 平台上开发非常简单,其允许所有 GUI 在 XML 中定义(尽管它也可以通过代码创建)的模型非常好。大多数图形示例都处理 View,但在大多数情况下,图表应该作为定义为布局 XML 的屏幕的一部分,因此在这里我们展示了在 ImageView 布局对象上的实现。

背景

在 Android 环境中,有一整套图形例程,通常一个 Bitmap 包含像素,一个 Canvas 是我们可以应用绘制调用的地方(写入位图),有了这个,我们可以使用 paint 绘制基元(文本、线条等),它描述了颜色、样式等。由于 Android 开发平台都是 Java,因此它的结构非常好,并且可以使用 Java 的所有功能,如继承、子类化等。对于我们的示例,一切都保持简单。

GUI 被定义为 XML 布局文件,随附的示例代码显示了 XY 图表的基本实现,为了简单起见,X 点是均匀间隔的顺序数字。

1. 定义 GUI 的 XML 布局文件

具有 ID 为 testy_img ImageView 对象是我们将用来绘制图表的那个

<?xml version="1.0" encoding="utf-8"?>

<TableLayout 

    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_height="fill_parent" 
    android:layout_width="fill_parent"
    android:background="#4B088A">
  
    <TableRow android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
     android:padding="20px"> 
   
     <TextView  
 	android:id="@+id/some" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Some layout  items here"
    android:layout_marginLeft="10px"
    android:layout_marginRight="10px"
    android:textColor="#ff8000"
    android:textStyle="bold"
    />

    </TableRow>

 <View     
  android:layout_width="fill_parent"
	android:layout_height="1dip"
	android:background="#FFE6E6E6"
	 />
	    
    <TableRow>

    <ImageView  
 	android:id="@+id/testy_img" 
 	android:layout_marginLeft="20px"	
 	 android:padding="20px"
    />

   </TableRow>

 <View     
  android:layout_width="fill_parent"
	android:layout_height="1dip"
	android:background="#FFE6E6E6" />

    <TableRow android:layout_width="fill_parent" 

    android:layout_height="wrap_content"
     android:padding="20px"> 

     <TextView  
 	android:id="@+id/more" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="More layout items here"
    android:layout_marginLeft="10px"
    android:layout_marginRight="10px"
   	android:textColor="#ff8000"
    android:textStyle="bold"
    />

    </TableRow>

</TableLayout>

2. 图表实现

要实现我们的图表,首先我们创建一个位图,我们将在其中写入,然后我们将其与布局 XML 对象关联。一旦我们有了位图,我们只需执行图表实现,在那里我们进行缩放、着色和数据处理。这个图表元素由一个网格组成,数据将在其中绘制,一个缩放/转置方法,以便可以将数据点连贯地映射到可用的屏幕空间,以及一个数据处理例程,用于循环遍历数据点。

2.1 定义要绘制的位图

如下所示,首先我们连接到 XML 布局中的对象,然后我们创建 Bitmap,我们将在其中进行所有绘制,这是由 quicky_XY 执行的,最后我们将此位图打印到图像上,从而打印到屏幕上。

 @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.testy); 
        setTitle("Quick XY Plot");
        
        ImageView image = (ImageView) findViewById(R.id.testy_img);
     
       Bitmap emptyBmap = Bitmap.createBitmap(250,
                200, Config.ARGB_8888); 
        
        int width =  emptyBmap.getWidth();
        int height = emptyBmap.getHeight();
        Bitmap charty = Bitmap.createBitmap(width , height , Bitmap.Config.ARGB_8888);
        
        charty = quicky_XY(emptyBmap);
       
         image.setImageBitmap(charty);     
  }

2.2 绘制用于绘图的网格

一旦定义了基本的 Bitmap,我们就需要相关的画布,这可以通过以下方式完成

Canvas canvas = new Canvas(bitmap) 

从那里开始,所有绘图都可以在画布上完成。我们需要网格来定义将绘制数据点的地方,一个图表通常会有一个标签和一些数据点范围。在所示的代码中,Vector 参数包含 [Label, units, maxValue, minValue] 形式的对象,完成后,drawSizes 数组将具有绘图区域的位置和尺寸。

public static void  draw_the_grid(Canvas this_g,  Vector these_labels)
     {         
        double rounded_max = 0.0;
        double rounded_min = 0.0;
        double rounded_max_temp;
        Object curElt;  
        String[] cur_elt_array;
        int left_margin_d, right_margin_d;      

        if( draw_only_this_idx == -1)      
           curElt = these_labels.elementAt(0);  // default  it to 1st one if non set 
        else
           curElt = these_labels.elementAt(draw_only_this_idx);  // now just the 1st elt
           
        cur_elt_array = (String[])curElt;

        rounded_max = get_ceiling_or_floor (Double.parseDouble(cur_elt_array[2]) , true);
        rounded_min = get_ceiling_or_floor (Double.parseDouble(cur_elt_array[3]) , false);

       // ok so now we have the max value of the set just get a cool ceiling and we go on
        final Paint paint = new Paint();  
        paint.setTextSize(15);
        
       left_margin_d =  getCurTextLengthInPixels(paint, Double.toString(rounded_max));
       //keep the position for later drawing -- leave space for the legend
       int p_height = 170;
       int p_width = 220;
       int[] tmp_draw_sizes = {2 + left_margin_d, 25,p_width - 2 - 
		left_margin_d ,p_height - 25 -5};
       drawSizes = tmp_draw_sizes; //keep it for later processing
        
        //with the mzrgins worked out draw the plotting grid
       paint.setStyle(Paint.Style.FILL); 
       paint.setColor(Color.WHITE );  
       
       // Android does by coords
       this_g.drawRect(drawSizes[0], drawSizes[1],drawSizes[0]+ 
		drawSizes[2], drawSizes[1]+ drawSizes[3] , paint);
       
       paint.setColor(Color.GRAY );       
       
        // finally draw the grid      
       
       paint.setStyle(Paint.Style.STROKE); 
       this_g.drawRect(drawSizes[0], drawSizes[1],drawSizes[0]+ 
		drawSizes[2], drawSizes[1]+ drawSizes[3] , paint);

           for(int i=1; i < 5 ; i++)
           {
               this_g.drawLine(drawSizes[0], drawSizes[1] + 
		(i * drawSizes[3] / 5), drawSizes[0] + drawSizes[2], 
		drawSizes[1] + (i * drawSizes[3] / 5), paint);
               this_g.drawLine(drawSizes[0]+ (i * drawSizes[2] / 5), 
		drawSizes[1], drawSizes[0] + (i * drawSizes[2] / 5), 
		drawSizes[1] + drawSizes[3], paint);
           }

          // good for one value
           print_axis_values_4_grid(this_g, cur_elt_array[1] , 
		Double.toString(rounded_max) , Double.toString(rounded_min), 
		cur_elt_array[0] , 2 ,0 );
         
     }  // --- end of draw_grid --- 

2.3. 绘图和缩放

数据点需要从数据范围到屏幕坐标的正确映射,这通过下面所示的 scale 方法完成。循环遍历数据点并在两个连续点之间调用 drawLine 将结束我们的 Chart。数据点将作为包含数据的 Vector 传递,现在将调用 plot_array_list

private static Point  scale_point(int this_x , double this_y  , Point drawPoint , 
            int scr_x  , int scr_y  , int scr_width  , int src_height  , 
            double maxX  , double minX  , double  maxY  , double minY  )
       {
           int temp_x, temp_y;
           Point temp = new Point();   
           
           if (maxY == minY)  //skip bad data
               return null;

           //don't touch it if is nothing
           try
           {
                   temp_x = scr_x + (int)( ((double)this_x - minX) * 
			((double)scr_width / (maxX - minX)) );
                   temp_y = scr_y + (int)( (maxY - this_y) * 
			((double)src_height / (maxY - minY)) );
                
                   temp.x = temp_x;
                   temp.y= temp_y;
                   drawPoint = temp;
           } 
           catch  (Exception e)
           {
        
              return (null);
           }
           
           return temp;
           
       } // --- end of scale_point --


    public static boolean plot_array_list(Canvas this_g, Vector this_array_list , 
	Vector these_labels , String this_title , int only_this_idx ) 
    {
             int idx;
             int lRow ;
             int nParms;
             int  i, points_2_plot, shifted_idx ; 
             int prev_x, prev_y ;
             int cur_x=0, cur_y=0 ; 
             //Dim ShowMarker As Object
             Point cur_point = new Point();
            cur_point.set(0,0);
     
             double cur_maxX, cur_minX, cur_maxY=20, cur_minY=0, cur_rangeY;
             int cur_start_x, cur_points_2_plot; 
   
            int POINTS_TO_CHANGE = 30;
            double cur_OBD_val;
  
             //Object curElt;  
             String curElt; 
             String[] cur_elt_array;
             Object curElt2;  
             String[] cur_elt_array2;
     
             final Paint paint = new Paint();

             try // catch in this block for some thing
             {       
                   points_2_plot = this_array_list.size();
                   {
                        cur_start_x = 0;
                        cur_points_2_plot = points_2_plot;
                        cur_maxX = cur_points_2_plot;
                        cur_minX = 0;
                   }
  
                   //'Create the plot points for this series from the ChartPoints array:
   
                   curElt = (String)this_array_list.elementAt(0);
                   
                   //the lines have to come out good
                    paint.setStyle(Paint.Style.STROKE);
//                  
                   //for(  nParms = 0 ; nParms < cur_elt_array.length ; nParms++ )
                   nParms = only_this_idx;
                   {
    
                       //get cur item labels
                        curElt2 = these_labels.elementAt(nParms);
                        cur_elt_array2  = (String[]) curElt2;
                        
                        cur_maxY = get_ceiling_or_floor 
			(Double.parseDouble(cur_elt_array2[2]) , true);
                        cur_minY = get_ceiling_or_floor 
			(Double.parseDouble(cur_elt_array2[3]) , false);
                        
                        cur_points_2_plot = this_array_list.size();
                        cur_maxX = cur_points_2_plot;
    
                      curElt = (String)this_array_list.elementAt(0);
                      cur_OBD_val = Double.parseDouble( curElt);
                      
                      cur_point = scale_point(0, cur_OBD_val, cur_point, 
                              drawSizes[0], drawSizes[1], drawSizes[2], drawSizes[3], 
                              cur_maxX, cur_minX, cur_maxY, 
		      cur_minY); //'(CInt(curAxisValues.Mins(nParms - 2) / 5) + 1) * 5)
                       
                       cur_x = cur_point.x;
                       cur_y = cur_point.y;

                       paint.setColor(Color.GREEN);
                       
                      // the point is only cool when samples are low
                       if ( cur_points_2_plot < POINTS_TO_CHANGE)
                         this_g.drawRect(cur_x-2, cur_y-2, cur_x-2 + 4,
						cur_y-2+ 4 , paint); 
                       
                       prev_x = cur_x;
                       prev_y = cur_y;

                       //'go and plot point for this parm -- pont after the 1st one 
                       for (lRow = cur_start_x +1 ; lRow< cur_start_x + 
					cur_points_2_plot -1 ; lRow++)
                       {        
                           curElt = (String)this_array_list.elementAt(lRow);
                           
                           cur_OBD_val = Double.parseDouble( curElt); 
                                 
                            //'work out an approx if cur Y values not avail(e.g. nothing)
                           // if (! (cur_elt_array[nParms ] == null ) )   //skip bad one
                             if( cur_OBD_val == Double.NaN) continue;  //skip bad one
                            {                  
       
                               
                                cur_point=scale_point(lRow, cur_OBD_val, cur_point,  
                                    drawSizes[0], drawSizes[1], 
				drawSizes[2], drawSizes[3], 
                                    cur_maxX, cur_minX, cur_maxY, cur_minY);
    
                                cur_x = cur_point.x;
                                cur_y = cur_point.y;
                                
                                if ( cur_points_2_plot < POINTS_TO_CHANGE)
                                   this_g.drawRect(cur_x-2, cur_y-2, cur_x-2 +4, 
				cur_y-2 + 4, paint ); 
 
                               this_g.drawLine( prev_x, prev_y, cur_x, cur_y, paint);
                               prev_x = cur_x;
                               prev_y = cur_y;
                          
                            } // ' if end of this_array(lRow, nParms - 1)<> nothing
                                 
                   } // end of for lrow
                               
               } // end of for nParmns

            //this_g.invalidate();
            return( true);
        }
        catch (Exception e)
        {
            return( false);
        }

    } // --- end of plot_array_list  --

这样,绘图就完成了,输出现在看起来像下图

android_xy.gif

最终想法

所示的方法运行良好,当我们确实需要进行实时绘图时,使用了例程的轻微修改,其中使用 Queue 数据结构代替了 Vector,因为我们需要能够绘制数据,因为它也具有滚动功能,但基本组件是相同的。可以根据要求应用更多修改。其中一种修改可以使用 drawLines 调用,但在这里我们需要设置 lines 数组参数,这可能与我们的 plot_array_list 方法没有太大区别。

历史

  • 2010 年 7 月 27 日:最初发布
© . All rights reserved.